package com.holly.unit.bpmn.activiti;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.holly.unit.auth.api.context.LoginContext;
import com.holly.unit.auth.api.pojo.login.LoginUser;
import com.holly.unit.bpmn.activiti.base.BaseProcessDefinitionDiagramLayoutResource;
import com.holly.unit.bpmn.activiti.base.DiagramViewerHighlightsRestResource;
import com.holly.unit.bpmn.activiti.context.ActivitiContext;
import com.holly.unit.bpmn.activiti.entity.*;
import com.holly.unit.bpmn.activiti.exception.BpmnActivitiException;
import com.holly.unit.bpmn.activiti.ext.*;
import com.holly.unit.bpmn.activiti.factory.BpmnModelFactory;
import com.holly.unit.bpmn.activiti.pojo.*;
import com.holly.unit.bpmn.activiti.pojo.dto.ActEntranceDTO;
import com.holly.unit.bpmn.activiti.pojo.dto.ActFromDTO;
import com.holly.unit.bpmn.activiti.util.ActUtil;
import com.holly.unit.bpmn.api.WorkflowApi;
import com.holly.unit.bpmn.api.constants.WorkflowConstants;
import com.holly.unit.bpmn.api.pojo.BpmnUser;
import com.holly.unit.bpmn.api.pojo.NewsTemplate;
import com.holly.unit.core.pojo.response.ErrorResponseData;
import com.holly.unit.core.pojo.response.ResponseData;
import com.holly.unit.core.pojo.response.SuccessResponseData;
import com.holly.unit.core.tree.factory.DefaultTreeBuildFactory;
import com.holly.unit.core.util.HttpServletUtil;
import com.holly.unit.db.api.factory.PageFactory;
import com.holly.unit.db.api.factory.PageResultFactory;
import com.holly.unit.db.api.pojo.page.PageResult;
import com.holly.unit.email.api.context.MailSenderContext;
import com.holly.unit.email.api.pojo.SendMailParam;
import com.holly.unit.message.api.MessageApi;
import com.holly.unit.message.api.context.MessageContext;
import com.holly.unit.message.api.pojo.request.MessageSendRequest;
import com.holly.unit.sms.api.context.SmsContext;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.activiti.bpmn.converter.BpmnXMLConverter;
import org.activiti.bpmn.model.Process;
import org.activiti.bpmn.model.*;
import org.activiti.editor.constants.ModelDataJsonConstants;
import org.activiti.editor.language.json.converter.BpmnJsonConverter;
import org.activiti.engine.*;
import org.activiti.engine.history.*;
import org.activiti.engine.impl.RepositoryServiceImpl;
import org.activiti.engine.impl.bpmn.behavior.UserTaskActivityBehavior;
import org.activiti.engine.impl.interceptor.Command;
import org.activiti.engine.impl.interceptor.CommandContext;
import org.activiti.engine.impl.javax.el.ExpressionFactory;
import org.activiti.engine.impl.javax.el.ValueExpression;
import org.activiti.engine.impl.juel.ExpressionFactoryImpl;
import org.activiti.engine.impl.juel.SimpleContext;
import org.activiti.engine.impl.persistence.entity.ExecutionEntity;
import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity;
import org.activiti.engine.impl.pvm.PvmActivity;
import org.activiti.engine.impl.pvm.PvmTransition;
import org.activiti.engine.impl.pvm.process.ActivityImpl;
import org.activiti.engine.impl.pvm.process.ProcessDefinitionImpl;
import org.activiti.engine.impl.task.TaskDefinition;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.Model;
import org.activiti.engine.repository.ModelQuery;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.Execution;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.runtime.ProcessInstanceQuery;
import org.activiti.engine.task.Comment;
import org.activiti.engine.task.IdentityLink;
import org.activiti.engine.task.Task;
import org.activiti.engine.task.TaskQuery;
import org.activiti.image.ProcessDiagramGenerator;
import org.activiti.validation.ValidationError;
import org.apache.batik.transcoder.TranscoderInput;
import org.apache.batik.transcoder.TranscoderOutput;
import org.apache.batik.transcoder.image.PNGTranscoder;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;

import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamReader;
import java.io.*;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 类描述: Activiti工作流操作类
 *
 * @author fxr
 * @version 1.0.0
 * @date 2022/1/27 15:48
 */
@Slf4j
public class ActivitiOperator implements WorkflowApi {


    private RepositoryService repositoryService = ActivitiContext.repositoryService();
    private HistoryService historyService = ActivitiContext.historyService();
    private RuntimeService runtimeService = ActivitiContext.runtimeService();
    private TaskService taskService = ActivitiContext.taskService();
    private ObjectMapper objectMapper = ActivitiContext.objectMapper();
    private IdentityService identityService = ActivitiContext.identityService();
    private ProcessEngineConfiguration processEngineConfiguration = ActivitiContext.processEngineConfiguration();
    private ManagementService managementService = ActivitiContext.managementService();


    // 扩展的Ext的流程定义，流程节点，流程条件
    private ActivitiZNodeService zNodeService = ActivitiContext.zNodeService();

    private ActivitiZProcessService zProcessService = ActivitiContext.zProcessService();

    private ActivitiZBusinessService zBusinessService = ActivitiContext.zBusinessService();

    private ActivitiZNewsTemplateService zNewsTemplateService = ActivitiContext.zNewsTemplateService();

    private ActivitiZNodeAuthService zNodeAuthService = ActivitiContext.zNodeAuthService();

    //入口管理api
    private ActEntranceServiceImpl actEntranceService = ActivitiContext.actEntranceService();
    //流程定义表单设置api
    private ActFromService fromService = ActivitiContext.actFromService();
    //流程定义表单设置api
    private ActiviExpressionService activiExpressionService = ActivitiContext.activiNodeService();
    //流程定义按钮设置api
    private ActivitiZButtonService activiButtonService = ActivitiContext.activiButtonService();


    // 1.模板资源服务[先考虑把这个方法放到这个位置]
    public String getStencilset() {

        InputStream stencilsetStream = this.getClass()
                .getClassLoader()
                .getResourceAsStream("static/activiti/stencilset.json");
        try {
            return IOUtils.toString(stencilsetStream, "utf-8");
        } catch (Exception e) {
            throw new ActivitiException("Error while loading stencil set", e);
        }
    }

    // 2. 网页流程编辑器元素
    public ObjectNode getEditorJson(String modelId) {
        ObjectNode modelNode = null;

        Model model = repositoryService.getModel(modelId);

        if (model != null) {
            try {
                if (StringUtils.isNotEmpty(model.getMetaInfo())) {
                    modelNode = (ObjectNode) objectMapper.readTree(model.getMetaInfo());
                } else {
                    modelNode = objectMapper.createObjectNode();
                    modelNode.put("name", model.getName());
                }
                modelNode.put("modelId", model.getId());
                ObjectNode editorJsonNode = (ObjectNode) objectMapper.readTree(
                        new String(repositoryService.getModelEditorSource(model.getId()), "utf-8"));
                modelNode.put("model", editorJsonNode);

            } catch (Exception e) {
                log.error("Error creating model JSON", e);
                throw new ActivitiException("Error creating model JSON", e);
            }
        }
        return modelNode;
    }

    // 3. 模型的保存方法
    public void saveModel(String modelId, ActivitiModelInfo modelInfo) {

        try {
            Model model = repositoryService.getModel(modelId);

            ObjectNode modelJson = (ObjectNode) objectMapper.readTree(model.getMetaInfo());

            modelJson.put("name", modelInfo.getName());
            modelJson.put("description", modelInfo.getDescription());
            model.setMetaInfo(modelJson.toString());
            model.setName(modelInfo.getName());

            repositoryService.saveModel(model);

            repositoryService.addModelEditorSource(model.getId(), modelInfo.getJson_xml().getBytes("utf-8"));

            InputStream svgStream = new ByteArrayInputStream(modelInfo.getSvg_xml().getBytes("utf-8"));
            TranscoderInput input = new TranscoderInput(svgStream);

            PNGTranscoder transcoder = new PNGTranscoder();
            // Setup output
            ByteArrayOutputStream outStream = new ByteArrayOutputStream();
            TranscoderOutput output = new TranscoderOutput(outStream);

            // Do the transformation
            transcoder.transcode(input, output);
            final byte[] result = outStream.toByteArray();
            repositoryService.addModelEditorSourceExtra(model.getId(), result);
            outStream.close();

        } catch (Exception e) {
            log.error("Error saving model", e);
            throw new ActivitiException("Error saving model", e);
        }

    }

    // 4.1 获取流程定义Diagram Viewer
    public ObjectNode getDiagramByDefinitionId(String processDefinitionId) {
        return BaseProcessDefinitionDiagramLayoutResource.getDiagramNode((String) null, processDefinitionId);
    }

    // 4.2 获取流程实例Diagram Viewer
    public ObjectNode getDiagramByInstanceId(String processInstanceId) {
        return BaseProcessDefinitionDiagramLayoutResource.getDiagramNode(processInstanceId, (String) null);
    }

    // 4.3 Diagram Viewer高亮
    public ObjectNode getHighlighted(String processInstanceId) {
        return DiagramViewerHighlightsRestResource.getHighlighted(processInstanceId);
    }

    /***
     * 流程模型列表查询方法
     * @param request 参数
     * @return
     */
    public PageResult<Model> modelListData(ActiviModelListRequest request) {
        ModelQuery modelQuery = repositoryService.createModelQuery();

        if (!Objects.isNull(request.getKeyWord())) {
            modelQuery.modelNameLike("%" + request.getKeyWord() + "%");
        }

        if (!Objects.isNull(request.getAppId())) {
            modelQuery.modelTenantId(request.getAppId());
        }

        List<Model> models = modelQuery.orderByCreateTime().desc().listPage((request.getPageNo()-1)*request.getPageSize(),request.getPageSize());
        Page<Model> objectPage = new Page<>();
        objectPage.setTotal(modelQuery.count());
        objectPage.setRecords(models);
        return PageResultFactory.createPageResult(objectPage);
    }

    public List<Model> modelPageData(ActiviModelListRequest request) {
        ModelQuery modelQuery = repositoryService.createModelQuery();

        if (!Objects.isNull(request.getKeyWord())) {
            modelQuery.modelNameLike("%" + request.getKeyWord() + "%");
        }

        if (!Objects.isNull(request.getAppId())) {
            modelQuery.modelTenantId(request.getAppId());
        }

        List<Model> models = modelQuery.orderByCreateTime().desc().list();
        return models;
    }


    /***
     * 流程模型创建
     * @param request
     */
    public Model newModel(NewModelRequest request) throws UnsupportedEncodingException {

        //初始化新模型
        Model model = repositoryService.newModel();

        String name = request.getName();
        String desc = request.getDescription();
        String key = request.getKey();
        String appId = request.getAppId();
        if (StrUtil.isEmpty(name)) name = "new-process";
        if (StringUtils.isEmpty(desc)) desc = "description";
        if (StringUtils.isEmpty(key)) key = "processKey";
        ObjectNode modelNode = objectMapper.createObjectNode();
        modelNode.put(ModelDataJsonConstants.MODEL_NAME, name);
        modelNode.put(ModelDataJsonConstants.MODEL_DESCRIPTION, desc);
        modelNode.put(ModelDataJsonConstants.MODEL_REVISION, 1);
        model.setName(name);
        model.setKey(key);
        model.setMetaInfo(modelNode.toString());
        //设置流程所属的系统
        model.setTenantId(appId);
        //保存到数据库中
        repositoryService.saveModel(model);
        String id = model.getId();
        //回填ModelEditorSource信息
        ObjectNode editorNode = objectMapper.createObjectNode();
        editorNode.put("id", "canvas");
        editorNode.put("resourceId", "canvas");
        ObjectNode stencilSetNode = objectMapper.createObjectNode();
        stencilSetNode.put("namespace",
                "http://b3mn.org/stencilset/bpmn2.0#");
        editorNode.put("stencilset", stencilSetNode);
        repositoryService.addModelEditorSource(id, editorNode.toString().getBytes("utf-8"));

        return model;

    }


    /***
     * 根据id 删除模型
     * @param id 模型id
     * @return
     */
    public String deleteMode(String id) {
        repositoryService.deleteModel(id);
        return id;
    }

    /***
     * 重构流程部署方法
     * @param id
     * @param routeName
     * @param tableName
     * @param categoryId
     * @param type
     */
    public ResponseData deploy2(String id, String routeName, String tableName, String categoryId, Integer type) {

        Model modelData = repositoryService.getModel(id);
        byte[] bytes = repositoryService.getModelEditorSource(modelData.getId());
        if (bytes == null) {
            return new ErrorResponseData("95001","模型数据为空，请先成功设计流程并保存");
        }
        try {
            JsonNode modelNode = new ObjectMapper().readTree(bytes);
            BpmnModel model = new BpmnJsonConverter().convertToBpmnModel(modelNode);
            List<ValidationError> validationErrors = repositoryService.validateProcess(model);
            if (validationErrors.size()>0) {
                return new ErrorResponseData("95002","模型校验有问题，请先成功设计流程并保存");
            }
            if(model.getProcesses().size()==0){
                return new ErrorResponseData("95003","模型不符要求，请至少设计一条主线流程");
            }
            byte[] bpmnBytes = new BpmnXMLConverter().convertToXML(model);
            // 部署发布模型流程
            String processName = modelData.getName() + ".bpmn20.xml";
            Deployment deployment = repositoryService.createDeployment()
                    .name(modelData.getName())
                    .addString(processName, new String(bpmnBytes, "UTF-8"))
                    .deploy();
            String metaInfo = modelData.getMetaInfo();
            JSONObject metaInfoMap = JSON.parseObject(metaInfo);
            // 设置流程分类 保存扩展流程至数据库
            List<ProcessDefinition> list = repositoryService.createProcessDefinitionQuery().deploymentId(deployment.getId()).list();
            for (ProcessDefinition pd : list) {
                ActivitiZProcess actZprocess = new ActivitiZProcess();
                actZprocess.setId(pd.getId());
                actZprocess.setName(modelData.getName());
                actZprocess.setProcessKey(modelData.getKey());
                actZprocess.setModelId(modelData.getId());
                actZprocess.setDeploymentId(deployment.getId());
                actZprocess.setDescription(metaInfoMap.getString(ModelDataJsonConstants.MODEL_DESCRIPTION));
                actZprocess.setVersion(pd.getVersion());
                actZprocess.setDiagramName(pd.getDiagramResourceName());
                zProcessService.setAllOldByProcessKey(modelData.getKey());
                actZprocess.setLatest(true);
                //关联应用id
                actZprocess.setAppid(modelData.getTenantId());
                actZprocess.setBusinessTable(tableName);
                actZprocess.setRouteName(routeName);
                actZprocess.setCategoryId(categoryId);
                actZprocess.setType(type);
                zProcessService.save(actZprocess);
            }
        }catch (Exception e) {
            String err = e.toString();
            log.error(e.getMessage(),e);
            if (err.indexOf("NCName")>-1){
                return new ErrorResponseData("95004","部署失败：流程设计中的流程名称不能为空，不能为数字以及特殊字符开头！");
            }
            if (err.indexOf("PRIMARY")>-1){
                return new ErrorResponseData("95005","部署失败：该模型已发布，key唯一！");
            }

            return new ErrorResponseData("95006","部署失败！");
        }
        return new SuccessResponseData();
    }


    /***
     * 组装流程图为树状结构VUE & React
     * @param modelId
     * @return
     * @throws Exception
     */
    public List<BpmnModelTreeNode> getflowsToVueOrReact(String modelId) throws Exception {
        // 定义返回结果
        List<BpmnModelTreeNode> treeNodeList = CollectionUtil.newArrayList();

        List<Map<String, String>> maps = this.getflowsToTree(modelId,"-1");
        // 组装节点
        for (Map<String, String> map : maps) {
            BpmnModelTreeNode treeNode = BpmnModelFactory.parseBpmnModelTreeNode(map);
            if ("2".equals(map.get("type"))) {
                String eleId = map.get("treeId");
                // 设置关联用户
                treeNode.setUsers(zNodeService.findUserByNodeId(eleId));
                // 设置关联角色
                treeNode.setRoles(zNodeService.findRoleByNodeId(eleId));
                // 设置关联部门
                treeNode.setDepartments(zNodeService.findDepartmentByNodeId(eleId));
                // 是否设置发起人部门负责人
                treeNode.setChooseDepHeader(zNodeService.hasChooseDepHeader(eleId));
                // 是否设置发起人
                treeNode.setChooseSponsor(zNodeService.hasChooseSponsor(eleId));
            }
            treeNodeList.add(treeNode);
        }
        // 构建树并返回
        return new DefaultTreeBuildFactory<BpmnModelTreeNode>().doTreeBuild(treeNodeList);
    }

    /***
     * 根据模型id获取流程元素列表【不包含子流程】
     * @param modelId
     * @return
     */
    public Collection<FlowElement> getFlowElementByModelId(String modelId) throws Exception{
        //转换
        JsonNode jsonNode = objectMapper.readTree(repositoryService.getModelEditorSource(modelId));
        BpmnModel bpmnModel = new BpmnJsonConverter().convertToBpmnModel(jsonNode);
        //取第一个流程,注：不包括子流程 待开发
        if(bpmnModel.getProcesses().size()<1){
            return null;
        }
        Process process = bpmnModel.getProcesses().get(0);
        Collection<FlowElement> flowElements = process.getFlowElements();
        return flowElements;
    }

    /***
     * 根据模型id获取流程模型
     * @param modelId
     * @return
     */
    public BpmnModel getBpmnModelByModelId(String modelId) throws Exception{
        //转换
        JsonNode jsonNode = objectMapper.readTree(repositoryService.getModelEditorSource(modelId));
        BpmnModel bpmnModel = new BpmnJsonConverter().convertToBpmnModel(jsonNode);
        return bpmnModel;
    }



    /***
     * 组装流程图为树状结构
     * @param modelId
     * @return
     * @throws Exception
     */
    public List<Map<String,String>> getflowsToTree(String modelId,String root) throws Exception {
        String contextPath = HttpServletUtil.getRequest().getContextPath();
        //转换
        JsonNode jsonNode = objectMapper.readTree(repositoryService.getModelEditorSource(modelId));
        BpmnModel bpmnModel = new BpmnJsonConverter().convertToBpmnModel(jsonNode);
        //取第一个流程,注：不包括子流程 待开发
        if(bpmnModel.getProcesses().size()<1){
            return null;
        }
        Process process = bpmnModel.getProcesses().get(0);
        Collection<FlowElement> flowElements = process.getFlowElements();
        //取得其中关键数据
        List<Map<String,String>> lists=new ArrayList<>();
        Map<String,String> tempmap=null;
        Map<String, Map<String,String>> allmap=new HashMap<>();
        for (FlowElement flowElement : flowElements) {
            tempmap=new HashMap<>();
            tempmap.put("treeId", flowElement.getId());
            tempmap.put("modelId", modelId);
            if(flowElement instanceof StartEvent){
                tempmap.put("treeName", "开始节点");
                tempmap.put("type", "1");
                tempmap.put("icon", contextPath+"/images/sys/none.png");
            }else if(flowElement instanceof UserTask){
                tempmap.put("type", "2");
                tempmap.put("treeName",flowElement.getName());
                tempmap.put("icon", contextPath+"/images/sys/typeuser.png");
            }else if(flowElement instanceof ExclusiveGateway || flowElement instanceof ParallelGateway){
                tempmap.put("type", "3");
                tempmap.put("treeName",StrUtil.isEmpty(flowElement.getName())?"网关":flowElement.getName());
                tempmap.put("icon", contextPath+"/images/sys/exclusive.png");
            } else if(flowElement instanceof SequenceFlow){
                tempmap.put("type", "4");
                tempmap.put("treeName",StrUtil.isEmpty(flowElement.getName())?"连线":flowElement.getName());
                tempmap.put("icon", contextPath+"/images/sys/sequenceflow.png");
            }else if(flowElement instanceof EndEvent){
                tempmap.put("type", "5");
                if(StrUtil.isNotEmpty(flowElement.getName())){
                    tempmap.put("treeName",flowElement.getName());
                }else{
                    tempmap.put("treeName","结束");
                }
                tempmap.put("icon", contextPath+"/images/sys/endnone.png");
            }
            String pid=root;
            if(flowElement instanceof SequenceFlow){
                pid=((SequenceFlow) flowElement).getSourceRef();
                tempmap.put("tarid", ((SequenceFlow) flowElement).getTargetRef());
                lists.add(tempmap);
            }else{
                List<SequenceFlow> sqlist= ((FlowNode) flowElement).getIncomingFlows();
                if(sqlist!=null&&sqlist.size()>0){
                    SequenceFlow tem1=sqlist.get(0);
                    pid=tem1.getSourceRef();
                }
            }
            tempmap.put("treePid", pid);
            allmap.put(flowElement.getId(),tempmap);
        }
        for(Map<String,String> map:lists){
            String pid=map.get("treePid");
            //如果该元素的父节点不为空 ，且父节点是 分支类型的
            if(allmap.get(pid)!=null&&"3".equals(allmap.get(pid).get("type"))){
                allmap.get(map.get("tarid")).put("treePid", map.get("treeId"));
            }else{
                allmap.remove( map.get("treeId"));
            }
        }
        lists.clear();
        for (Map.Entry<String, Map<String, String>> entry : allmap.entrySet()) {
            String typex=entry.getValue().get("type");
            lists.add(entry.getValue());
        }
        return lists;
    }

    /***
     * 组装流程图为树状结构支持Ztree
     * @param modelId
     * @return
     * @throws Exception
     */
    public List<Map<String,String>> getflowsToZtree(String modelId) throws Exception {
        return getflowsToTree(modelId,"0");
    }

    /***
     * 根据流程模型id获取流程图信息【比如坐标，名称】
     * @param id
     */
    public List<Map<String,Object>> fetchBpmnModelById(String id) throws Exception{
        //前端页面显示数据
        List<Map<String,Object>> list = new ArrayList<>();
        Model modelData = repositoryService.getModel(id);
        byte[] bytes = repositoryService.getModelEditorSource(modelData.getId());
        JsonNode modelNode = new ObjectMapper().readTree(bytes);
        BpmnModel bpmnModel = new BpmnJsonConverter().convertToBpmnModel(modelNode);
        List<Process> processes = bpmnModel.getProcesses();
        if(processes==null||processes.size()==0){
            return null;
        }

        for (Process process : processes) {
            Collection<FlowElement> elements = process.getFlowElements();
            //取得其中关键数据
            List<Map<String,String>> lists=new ArrayList<>();
            Map<String, Map<String,String>> allmap=new HashMap<>();
            for (FlowElement flowElement:elements) {
                 if(flowElement instanceof SequenceFlow){//如果是连线，就把连线的节点id
                     Map<String,Object> map = new HashMap<>();
                     String sourceRef = ((SequenceFlow) flowElement).getSourceRef();
                     FlowElement sourceFlowElement = ActUtil.getFlowElementByNodeId(id,sourceRef);
                     String sourceName = "";
                     if (sourceFlowElement instanceof ExclusiveGateway) {
                         sourceName="排他网关";

                     } else if (sourceFlowElement instanceof ParallelGateway) {
                         sourceName="并行网关";

                     } else if (sourceFlowElement instanceof StartEvent) {
                         sourceName="开始节点";
                     } else if (sourceFlowElement instanceof EndEvent) {
                         sourceName="结束节点";
                     } else if (sourceFlowElement instanceof UserTask) {
                         sourceName = sourceFlowElement.getName();
                     }

                     String targetRef = ((SequenceFlow) flowElement).getTargetRef();
                     FlowElement targetFlowElement = ActUtil.getFlowElementByNodeId(id,targetRef);
                     String targetName = "";

                     if (targetFlowElement instanceof ExclusiveGateway) {
                         targetName="排他网关";
                     } else if (targetFlowElement instanceof ParallelGateway) {
                         targetName="并行网关";
                     } else if (targetFlowElement instanceof StartEvent) {
                         targetName="开始节点";
                     } else if (targetFlowElement instanceof EndEvent) {
                         targetName="结束节点";
                     } else if (targetFlowElement instanceof UserTask) {
                         targetName = targetFlowElement.getName();
                     }

                     String self = flowElement.getId();
                     map.put("sourceRef",sourceRef);
                     map.put("sourceName",sourceName);
                     map.put("targetRef",targetRef);
                     map.put("targetName",targetName);
                     map.put("self",self);
                     list.add(map);
                 }
            }
        }
        return list;
    }



    /***
     * 流程部署
     * @param id
     */
    public String deploy(String id) {
        String res = "";
        // 获取模型
        Model modelData = repositoryService.getModel(id);
        byte[] bytes = repositoryService.getModelEditorSource(modelData.getId());


        if (bytes == null) {
            return "模型数据为空，请先成功设计流程并保存";
        }

        try{
            JsonNode modelNode = new ObjectMapper().readTree(bytes);
            BpmnModel model = new BpmnJsonConverter().convertToBpmnModel(modelNode);
            if(model.getProcesses().size()==0){
                return "模型不符要求，请至少设计一条主线流程";
            }
            byte[] bpmnBytes = new BpmnXMLConverter().convertToXML(model);

            // 部署发布模型流程
            String processName = modelData.getName() + ".bpmn20.xml";
            Deployment deployment = repositoryService.createDeployment()
                    .name(modelData.getName())
                    .addString(processName, new String(bpmnBytes, "UTF-8"))
                    .deploy();
            String metaInfo = modelData.getMetaInfo();
            JSONObject metaInfoMap = JSON.parseObject(metaInfo);
            // 设置流程分类 保存扩展流程至数据库
            List<ProcessDefinition> list = repositoryService.createProcessDefinitionQuery().deploymentId(deployment.getId()).list();
            for (ProcessDefinition pd : list) {
                ActivitiZProcess actZprocess = new ActivitiZProcess();
                actZprocess.setId(pd.getId());
                actZprocess.setName(modelData.getName());
                actZprocess.setProcessKey(modelData.getKey());
                actZprocess.setModelId(modelData.getId());
                actZprocess.setDeploymentId(deployment.getId());
                actZprocess.setDescription(metaInfoMap.getString(ModelDataJsonConstants.MODEL_DESCRIPTION));
                actZprocess.setVersion(pd.getVersion());
                actZprocess.setDiagramName(pd.getDiagramResourceName());
                zProcessService.setAllOldByProcessKey(modelData.getKey());
                actZprocess.setLatest(true);
                //关联应用id
                actZprocess.setAppid(modelData.getTenantId());
                zProcessService.save(actZprocess);
            }
        }catch (Exception e) {
            String err = e.toString();
            log.error(e.getMessage(),e);
            if (err.indexOf("NCName")>-1){
                return "部署失败：流程设计中的流程名称不能为空，不能为数字以及特殊字符开头！";
            }
            if (err.indexOf("PRIMARY")>-1){
                return "部署失败：该模型已发布，key唯一！";
            }
            return "部署失败！";
        }

        res = "部署成功";
        return res;
    }

    /***
     * 流程模型导出
     * @param id 流程模型id
     * @return
     */
    public Map<String,Object> export(String id) {
        Map<String,Object> map = new HashMap<>();
        ProcessDefinition pd = repositoryService.createProcessDefinitionQuery()
                .processDefinitionId(id).singleResult();

        String resourceName = pd.getDiagramResourceName();
        InputStream inputStream = repositoryService.getResourceAsStream(pd.getDeploymentId(),
                resourceName);
        map.put("src",resourceName);
        map.put("in",inputStream);
        return map;
    }

    /***
     * 高亮实时流程图
     * @param id
     * @return
     */
    public Map<String,Object> highLight(String id) {
        Map<String,Object> res = new HashMap<>();

        InputStream inputStream = null;
        ProcessInstance pi = null;
        String picName = "";
        // 查询历史
        HistoricProcessInstance hpi = historyService.createHistoricProcessInstanceQuery().processInstanceId(id).singleResult();
        if (hpi.getEndTime() != null) {
            // 已经结束流程获取原图
            ProcessDefinition pd = repositoryService.createProcessDefinitionQuery().processDefinitionId(hpi.getProcessDefinitionId()).singleResult();
            picName = pd.getDiagramResourceName();
            inputStream = repositoryService.getResourceAsStream(pd.getDeploymentId(), pd.getDiagramResourceName());
        } else {
            pi = runtimeService.createProcessInstanceQuery().processInstanceId(id).singleResult();
            BpmnModel bpmnModel = repositoryService.getBpmnModel(pi.getProcessDefinitionId());

            List<String> highLightedActivities = new ArrayList<String>();
            // 高亮任务节点
            List<Task> tasks = taskService.createTaskQuery().processInstanceId(id).list();
            for (Task task : tasks) {
                highLightedActivities.add(task.getTaskDefinitionKey());
            }

            List<String> highLightedFlows = new ArrayList<String>();
            ProcessDiagramGenerator diagramGenerator = processEngineConfiguration.getProcessDiagramGenerator();
            //"宋体"
            inputStream = diagramGenerator.generateDiagram(bpmnModel, "png", highLightedActivities, highLightedFlows,
                    "宋体", "宋体", "宋体",null, 1.0);
            picName = pi.getName()+".png";
        }
        res.put("picName",picName);
        res.put("in",inputStream);
        return res;
    }

    /***
     * 查看XML
     * @param deploymentId
     * @return
     */
    public InputStream getBpmnXml(String deploymentId) {

        ProcessDefinition pd = repositoryService.createProcessDefinitionQuery()
                .processDefinitionId(deploymentId).singleResult();
        String resourceName = pd.getDiagramResourceName();

        InputStream inputStream = repositoryService.getResourceAsStream(deploymentId,resourceName);
        return inputStream;
    }

    /***
     * 导出BPMNXML
     * @param deploymentId
     * @return
     * @throws Exception
     */
    public BpmnModel exportBpmnXml(String deploymentId) throws Exception{
        Model modelData = repositoryService.getModel(deploymentId);
        BpmnJsonConverter jsonConverter = new BpmnJsonConverter();
        JsonNode editorNode = new ObjectMapper().readTree(repositoryService.getModelEditorSource(modelData.getId()));
        BpmnModel bpmnModel = jsonConverter.convertToBpmnModel(editorNode);

        return bpmnModel;
    }

    // 流程跟踪url
    public String fetchTrackUrl(String ip,String port,String contentPath,String id) {
        //路径url
        String url = "";
//        String ip = NetWorkUtil.getInet4Address();
        //根据business表查询流程定义的id
        ActivitiZBusiness activitiBusiness = zBusinessService.lambdaQuery().select().eq(ActivitiZBusiness::getProcInstId, id).one();
        String processDefiniedId = activitiBusiness.getProcDefId();
        //url+="http://"+ip+":"+port+contentPath+"/activiti/diagram-viewer/index.html?processDefinitionId="+processDefiniedId+"&processInstanceId="+id;
        url+="/activiti/diagram-viewer/index.html?processDefinitionId="+processDefiniedId+"&processInstanceId="+id;
        return url;
    }

    //////////////////////////////////////流程定义-开始/////////////////////////////////////////////////


    /***
     * 根据流程定义的id，获取第一个节点信息
     * @param procDefId 流程定义id
     * @return
     */
    public ProcessNode getFirstNode(String procDefId,String key,String value) {

        BpmnModel bpmnModel = repositoryService.getBpmnModel(procDefId);

        ProcessNode node = new ProcessNode();

        List<Process> processes = bpmnModel.getProcesses();
        Collection<FlowElement> elements = processes.get(0).getFlowElements();
        // 流程开始节点
        StartEvent startEvent = null;
        for (FlowElement element : elements) {
            if (element instanceof StartEvent) {
                startEvent = (StartEvent) element;
                break;
            }
        }
        FlowElement e = null;
        if (startEvent.getOutgoingFlows().size()>0) {
            // 开始节点后不止一个用户任务节点
            List<SequenceFlow> sfs = startEvent.getOutgoingFlows();
            for (SequenceFlow sf:sfs) {
                String el = sf.getConditionExpression();
                if (StrUtil.isEmptyIfStr(el) || StrUtil.isEmptyIfStr(key) || StrUtil.isEmptyIfStr(value)) {// 如果连线上没有配置表达式，默认选取一个
                    String targetId = sf.getTargetRef();
                    for (FlowElement element : elements) {
                        if(element.getId().equals(targetId)){
                            if(element instanceof UserTask){
                                e = element;
                                node.setType(1);
                                break;
                            }else if(element instanceof ExclusiveGateway){
                                e = element;
                                node.setType(3);
                                break;
                            }else if(element instanceof ParallelGateway){
                                e = element;
                                node.setType(4);
                                break;
                            }else{
                                throw new RuntimeException("流程设计错误，开始节点后只能是用户任务节点、排他网关、并行网关");
                            }
                        }
                    }
                    // 排他、平行网关直接返回
                    if(e instanceof ExclusiveGateway || e instanceof ParallelGateway){
                        return node;
                    }
                    node.setTitle(e.getName());
                    // 设置关联用户
                    List<LoginUser> users = getNodetUsers(e.getId());
                    node.setUsers(removeDuplicate(users));
                    break;
                } else {
                    boolean result = isCondition(key,el,value);
                    if (result) {//判断条件通过
                        String targetId = sf.getTargetRef();
                        for (FlowElement element : elements) {
                            if(element.getId().equals(targetId)){
                                if(element instanceof UserTask){
                                    e = element;
                                    node.setType(1);
                                    break;
                                }else if(element instanceof ExclusiveGateway){
                                    e = element;
                                    node.setType(3);
                                    break;
                                }else if(element instanceof ParallelGateway){
                                    e = element;
                                    node.setType(4);
                                    break;
                                }else{
                                    throw new RuntimeException("流程设计错误，开始节点后只能是用户任务节点、排他网关、并行网关");
                                }
                            }
                        }
                        // 排他、平行网关直接返回
                        if(e instanceof ExclusiveGateway || e instanceof ParallelGateway){
                            return node;
                        }
                        node.setTitle(e.getName());
                        // 设置关联用户
                        List<LoginUser> users = getNodetUsers(e.getId());
                        node.setUsers(removeDuplicate(users));
                    }
                }

            }
            return node;
        }
        // 判断开始后的流向节点
        SequenceFlow sequenceFlow = startEvent.getOutgoingFlows().get(0);
        for (FlowElement element : elements) {
            if(element.getId().equals(sequenceFlow.getTargetRef())){
                if(element instanceof UserTask){
                    e = element;
                    node.setType(1);
                    break;
                }else if(element instanceof ExclusiveGateway){
                    e = element;
                    node.setType(3);
                    break;
                }else if(element instanceof ParallelGateway){
                    e = element;
                    node.setType(4);
                    break;
                }else{
                    throw new RuntimeException("流程设计错误，开始节点后只能是用户任务节点、排他网关、并行网关");
                }
            }
        }
        // 排他、平行网关直接返回
        if(e instanceof ExclusiveGateway || e instanceof ParallelGateway){
            return node;
        }
        node.setTitle(e.getName());
        // 设置关联用户
        List<LoginUser> users = getNodetUsers(e.getId());
        node.setUsers(removeDuplicate(users));
        return node;

    }



    // 获取所有的用户
    public List<LoginUser> fetchAllUser() {
        return zNodeService.queryAllUser();
    }

    /***
     * 获取流程定义列表
     * @param queryMap 参数
     * @return
     */
    public PageResult<ActProcessRequest> listData(Map<String,Object> queryMap) {
        return zProcessService.actZProcess(queryMap);
    }

    /***
     * 激活或挂起流程定义
     * @param id 流程定义id
     * @param status 状态1表示 激活
     */
    public void activateOrSuspend (String id, Integer status) {
        boolean flag = zProcessService.whetherSetUpForm(id);
        if (flag) {
            if (status == 1) {
                repositoryService.activateProcessDefinitionById(id, true, new Date());
            } else {
                repositoryService.suspendProcessDefinitionById(id, true, new Date());
            }
            ActivitiZProcess actProcess = zProcessService.getById(id);
            actProcess.setStatus(status);
            zProcessService.updateById(actProcess);

            actEntranceService.updateStatus(id,status);
        } else {
            throw new BpmnActivitiException("未设置关联表单，点击编辑设置。");
        }
    }


    /***
     * 通过流程定义id 查询出流程模型数据 element.getId()对应act_z_node node_id存储的字段
     * @param processId 流程定义id
     */
    public void deleteNodeUsers(String processId){

        BpmnModel bpmnModel = repositoryService.getBpmnModel(processId);
        List<Process> processes = bpmnModel.getProcesses();
        for(Process process : processes){
            Collection<FlowElement> elements = process.getFlowElements();
            for(FlowElement element : elements) {
                zNodeService.deleteByNodeId(element.getId());
            }
        }
    }


    /***
     * 通过id删除流程定义
     * @param ids
     */
    public boolean delByIds(String ids) {
        for (String id:ids.split(",")) {
            if(CollectionUtil.isNotEmpty(zBusinessService.findByProcDefId(id))){
                log.info("包含已发起申请的流程，无法删除");
                return false;
            }
            ActivitiZProcess actProcess = zProcessService.getById(id);
            // 当删除最后一个版本时 删除关联数据
            if (actProcess == null) {
                log.info("该数据已删除！");
                return false;
            }
            if(actProcess.getVersion()==1){
                deleteNodeUsers(id);
            }
            // 级联删除
            repositoryService.deleteDeployment(actProcess.getDeploymentId(), true);
            zProcessService.removeById(id);
            // 更新最新版本
            zProcessService.setLatestByProcessKey(actProcess.getProcessKey());
        }
        return true;
    }

    /***
     * 流程定义转化成流程模型
     * @param id
     * @throws Exception
     */
    public void convertToModel(String id) throws Exception{
        ProcessDefinition pd = repositoryService.createProcessDefinitionQuery().processDefinitionId(id).singleResult();
        InputStream bpmnStream = repositoryService.getResourceAsStream(pd.getDeploymentId(), pd.getResourceName());
        ActivitiZProcess actProcess = zProcessService.getById(id);

        XMLInputFactory xif = XMLInputFactory.newInstance();
        InputStreamReader in = new InputStreamReader(bpmnStream, "UTF-8");
        XMLStreamReader xtr = xif.createXMLStreamReader(in);
        BpmnModel bpmnModel = new BpmnXMLConverter().convertToBpmnModel(xtr);
        BpmnJsonConverter converter = new BpmnJsonConverter();

        ObjectNode modelNode = converter.convertToJson(bpmnModel);
        Model modelData = repositoryService.newModel();
        modelData.setKey(pd.getKey());
        modelData.setName(pd.getResourceName());

        ObjectNode modelObjectNode = new ObjectMapper().createObjectNode();
        modelObjectNode.put(ModelDataJsonConstants.MODEL_NAME, actProcess.getName());
        modelObjectNode.put(ModelDataJsonConstants.MODEL_REVISION, modelData.getVersion());
        modelObjectNode.put(ModelDataJsonConstants.MODEL_DESCRIPTION, actProcess.getDescription());
        modelData.setMetaInfo(modelObjectNode.toString());

        repositoryService.saveModel(modelData);
        repositoryService.addModelEditorSource(modelData.getId(), modelNode.toString().getBytes("utf-8"));
    }

    /***
     * 修改流程定义扩展信息
     * @param actProcess 流程定义扩展信息
     */
    public boolean updateZProcess(ActivitiZProcess actProcess) {
        ProcessDefinition pd = repositoryService.getProcessDefinition(actProcess.getId());
        if(pd==null){
            log.info("流程定义不存在");
            return false;
        }
        if(StrUtil.isNotBlank(actProcess.getCategoryId())){
            repositoryService.setProcessDefinitionCategory(actProcess.getId(), actProcess.getCategoryId());
            repositoryService.setDeploymentCategory(pd.getDeploymentId(), actProcess.getCategoryId());
            actEntranceService.updateCategory(actProcess.getId(),actProcess.getCategoryId());
        }
        return zProcessService.updateById(actProcess);
    }

    /***
     * 通过流程定义id获取流程节点
     * @param id 流程定义id
     * @return
     */
    public List<ProcessNode> fetchProcessNode(String id) {

        //获取流程模型资源
        BpmnModel bpmnModel = repositoryService.getBpmnModel(id);

        //前端页面显示数据
        List<ProcessNode> list = new ArrayList<>();

        List<Process> processes = bpmnModel.getProcesses();
        if(processes==null||processes.size()==0){
            return null;
        }

        for (Process process : processes) {
            Collection<FlowElement> elements = process.getFlowElements();
            //取得其中关键数据
            List<Map<String,String>> lists=new ArrayList<>();
            Map<String,String> tempmap=null;
            Map<String, Map<String,String>> allmap=new HashMap<>();
            for (FlowElement flowElement:elements) {
                tempmap=new HashMap<>();
                tempmap.put("treeId", flowElement.getId());
                tempmap.put("modelId", id);
                if(flowElement instanceof StartEvent){
                    tempmap.put("treeName", "开始节点");
                    tempmap.put("type", "0");

                }else if(flowElement instanceof UserTask){
                    tempmap.put("type", "1");
                    tempmap.put("treeName",flowElement.getName());

                }else if(flowElement instanceof ExclusiveGateway){
                    tempmap.put("type", "3");
                    tempmap.put("treeName",flowElement.getName());

                } else if (flowElement instanceof ParallelGateway) {
                    tempmap.put("type", "3");
                    tempmap.put("treeName",flowElement.getName());
                }else if(flowElement instanceof SequenceFlow){
                    tempmap.put("type", "5");
                    tempmap.put("treeName",flowElement.getName());
                }else if(flowElement instanceof EndEvent){
                    tempmap.put("type", "2");
                    if(StringUtils.isNotEmpty(flowElement.getName())){
                        tempmap.put("treeName",flowElement.getName());
                    }else{
                        tempmap.put("treeName","结束");
                    }

                }
                String pid="0";
                if(flowElement instanceof SequenceFlow){
                    pid=((SequenceFlow) flowElement).getSourceRef();
                    tempmap.put("tarid", ((SequenceFlow) flowElement).getTargetRef());
                    lists.add(tempmap);
                }else{
                    List<SequenceFlow> sqlist= ((FlowNode) flowElement).getIncomingFlows();
                    if(sqlist!=null&&sqlist.size()>0){
                        SequenceFlow tem1=sqlist.get(0);
                        pid=tem1.getSourceRef();
                    }
                }
                tempmap.put("treePid", pid);
                allmap.put(flowElement.getId(),tempmap);
            }

            //处理数据
            for(Map<String,String> map:lists){
                String pid=map.get("treePid");
                //如果该元素的父节点不为空 ，且父节点是 分支类型的
                if(allmap.get(pid)!=null&&"3".equals(allmap.get(pid).get("type"))){
                    allmap.get(map.get("tarid")).put("treePid", map.get("treeId"));
                }else{
                    allmap.remove( map.get("treeId"));
                }
            }
            lists.clear();
            for (Map.Entry<String, Map<String, String>> entry : allmap.entrySet()) {
                lists.add(entry.getValue());
            }
            for (Map<String,String> map:lists) {
                ProcessNode node = new ProcessNode();
                String eleId = map.get("treeId");
                String eleName = map.get("treeName");
                node.setId(eleId);
                node.setTitle(eleName);
                if ("0".equals(map.get("type"))) {//开始节点
                    node.setType(Integer.valueOf(map.get("type")));
                } else if ("1".equals(map.get("type"))) {//用户任务
                    node.setType(Integer.valueOf(map.get("type")));
                    // 设置关联用户
                    node.setUsers(zNodeService.findUserByNodeId(eleId));
                    // 设置关联角色
                    node.setRoles(zNodeService.findRoleByNodeId(eleId));
                    // 设置关联部门
                    node.setDepartments(zNodeService.findDepartmentByNodeId(eleId));
                    // 是否设置发起人部门负责人
                    node.setChooseDepHeader(zNodeService.hasChooseDepHeader(eleId));
                    // 是否设置发起人
                    node.setChooseSponsor(zNodeService.hasChooseSponsor(eleId));
                } else if ("2".equals(map.get("type"))) {//结束任务
                    node.setType(Integer.valueOf(map.get("type")));
                } else if ("3".equals(map.get("type"))) {//排他网关
                    node.setType(Integer.valueOf(map.get("type")));
                    node.setTitle("网关");
                } else if ("4".equals(map.get("type"))) {//并行网关
                    node.setType(Integer.valueOf(map.get("type")));
                    node.setTitle("网关");
                } else if ("5".equals(map.get("type"))) {//连线
                    node.setType(Integer.valueOf(map.get("type")));
                } else {
                    //排除其他链接或者节点
                    continue;
                }
                list.add(node);
            }
        }
        list.sort(Comparator.comparing(ProcessNode::getType));
        return list;
    }

    @Override
    public List<Map<String, Object>> fetchProcessNodesByProcessDefinitionId(String processDefinitionId) {
        BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId);
        if (null == bpmnModel){
            System.out.println("模型数据为空，模型不存在");
        }
        List<Process> processList = bpmnModel.getProcesses();
        if (CollectionUtil.isNotEmpty(processList)) {
            List<Map<String,Object>> list = new ArrayList<>();
            processList.forEach(process -> {
                Collection<FlowElement> elementList = process.getFlowElements();
                for (FlowElement flowElement : elementList) {
                    Map<String,Object> element = new HashMap<>();
                    element.put("id",flowElement.getId());
                    element.put("name",StrUtil.isBlank(flowElement.getName())?"":flowElement.getName());
                    if (flowElement instanceof StartEvent){
                        element.put("StartEvent","StartEvent");
                    }else if (flowElement instanceof UserTask){
                        element.put("UserTask","UserTask");
                    }else if (flowElement instanceof EndEvent){
                        element.put("EndEvent","EndEvent");
                    }else if (flowElement instanceof SequenceFlow){
                        // 流程连接线
                        continue;
                    }else if (flowElement instanceof ExclusiveGateway){
                        // 流程网关
                        continue;
                    }
                    list.add(element);
                }
            });
            return list;
        }
        return null;
    }

    @Override
    public Map<String, Object> fetchStartEventNodeByProcessDefinitionId(String processDefinitionId) {
        Map<String,Object> rst = new HashMap<>();
        BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId);
        if (null == bpmnModel){
            log.info("模型数据为空，模型不存在");
        }
        List<Process> processList = bpmnModel.getProcesses();
        if (CollectionUtil.isNotEmpty(processList)) {
            List<Map<String,Object>> list = new ArrayList<>();
            processList.forEach(process -> {
                Collection<FlowElement> elementList = process.getFlowElements();
                for (FlowElement flowElement : elementList) {
                    Map<String,Object> element = new HashMap<>();
                    element.put("id",flowElement.getId());
                    element.put("name",StrUtil.isBlank(flowElement.getName())?"":flowElement.getName());
                    if (flowElement instanceof StartEvent){
                        element.put("type","StartEvent");
                    }else if (flowElement instanceof UserTask){
                        element.put("type","UserTask");
                    }else if (flowElement instanceof EndEvent){
                        element.put("type","EndEvent");
                    }else if (flowElement instanceof SequenceFlow){
                        // 流程连接线
                        continue;
                    }else if (flowElement instanceof ExclusiveGateway){
                        // 流程网关
                        continue;
                    }
                    list.add(element);
                }
            });

            if (list != null && list.size()>0) {
                for (Map<String,Object> map:list) {
                    if (map.get("type").equals("StartEvent")) {
                        rst = map;
                        break;
                    }
                }
            }
        }
        return rst;
    }


    /***
     * 编辑节点分配用户
     * @param nodeId
     * @param userIds
     * @param roleIds
     * @param departmentIds
     * @param chooseDepHeader
     * @param chooseSponsor 是否勾选操作人的部门负责人
     * @throws Exception
     */
    public void editNodeUser(String nodeId, String userIds, String roleIds, String departmentIds, Boolean chooseDepHeader, Boolean chooseSponsor) throws Exception{
        // 删除其关联权限
        zNodeService.deleteByNodeId(nodeId);
        // 分配新用户
        for(String userId : userIds.split(",")){
            ActivitiZNode actNode = new ActivitiZNode();
            actNode.setNodeId(nodeId);
            actNode.setRelateId(userId);
            actNode.setType(1);
            zNodeService.save(actNode);
        }
        // 分配新角色
        for(String roleId : roleIds.split(",")){
            ActivitiZNode actNode = new ActivitiZNode();
            actNode.setNodeId(nodeId);
            actNode.setRelateId(roleId);
            actNode.setType(0);
            zNodeService.save(actNode);
        }
        // 分配新部门
        for(String departmentId : departmentIds.split(",")){
            ActivitiZNode actNode = new ActivitiZNode();
            actNode.setNodeId(nodeId);
            actNode.setRelateId(departmentId);
            actNode.setType(2);
            zNodeService.save(actNode);
        }
        if(chooseDepHeader!=null&&chooseDepHeader){
            ActivitiZNode actNode = new ActivitiZNode();
            actNode.setNodeId(nodeId);
            actNode.setType(4);
            zNodeService.save(actNode);
        }
        if(chooseSponsor!=null&&chooseSponsor){
            ActivitiZNode actNode = new ActivitiZNode();
            actNode.setNodeId(nodeId);
            actNode.setType(3);
            zNodeService.save(actNode);
        }
    }


    /***
     * 获取下一个流程节点
     * @param procDefId
     * @param currActId
     * @param procInsId
     * @return
     */
    public ProcessNode fetchNextNode2(String procDefId,String currActId,String procInsId,String tableId,String tableName,String executionId) {
        ProcessNode node = new ProcessNode();

        //获取所有节点
        final BpmnModel bpmnModel = repositoryService.getBpmnModel(procDefId);
        FlowElement flowElement = bpmnModel.getFlowElement(currActId);
        Activity activity = (Activity) flowElement;
        MultiInstanceLoopCharacteristics loopCharacteristics = activity.getLoopCharacteristics();
        boolean status = false;
        if (loopCharacteristics == null) {
            status = true;
        } else {

            // 节点为多实例的场合
            // 获取完成任务实例数量
            int nrOfCompletedInstances = (int) runtimeService.getVariable(executionId, "nrOfCompletedInstances");
            // 获取会签总实例数量
            int numberOfInstances = (int) runtimeService.getVariable(executionId, "nrOfInstances");
            if (numberOfInstances - nrOfCompletedInstances == 1) {
                status = true;
            }

        }

        if (status) {
            List<PvmActivity> getNextActNodes = ActUtil.getNextActNodes(procDefId,currActId,tableId,tableName);


            // 判断出当前流程所处节点，根据路径获得下一个节点实例
            for(PvmActivity pvmActivity : getNextActNodes){
                String type = pvmActivity.getProperty("type").toString();
                if("userTask".equals(type)){
                    // 用户任务节点
                    node.setType(WorkflowConstants.NODE_TYPE_TASK);
                    node.setTitle(pvmActivity.getProperty("name").toString());
                    // 设置关联用户
                    List<LoginUser> users = getNodetUsers(pvmActivity.getId());
                    //设置关联角色
                    //node.setRoles(zNodeService.findRoleByNodeId(pvmActivity.getId()));
                    // TODO 去重用户？？？？？？？？？？？？？？？
                    node.setUsers(removeDuplicate(users));
                }else if("exclusiveGateway".equals(type)){

                    // 用户任务节点
                    node.setType(WorkflowConstants.NODE_TYPE_TASK);
                    node.setTitle(pvmActivity.getProperty("name").toString());
                    // 设置关联用户
                    List<LoginUser> users = getNodetUsers(pvmActivity.getId());
                    //设置关联角色
                   // node.setRoles(zNodeService.findRoleByNodeId(pvmActivity.getId()));
                    // TODO 去重用户？？？？？？？？？？？？？？？
                    node.setUsers(removeDuplicate(users));
                }else if("parallelGateway".equals(type)){
                    // 平行网关
                    node.setType(WorkflowConstants.NODE_TYPE_PG);
                } else if ("scriptTask".equals(type)) {
                    //如果脚本任务在用户任务中间，就要解析脚本任务下一个用户任务的审批人
                    //脚本任务定义在流程的最后一端，直接出发执行
                } else if("endEvent".equals(type)){
                    // 结束
                    node.setType(WorkflowConstants.NODE_TYPE_END);
                }else{
                    throw new BpmnActivitiException("流程设计错误，包含无法处理的节点");
                }
                break;
            }
        }
        return node;
    }

    /***
     * 获取下一个流程节点
     * @param procDefId
     * @param currActId
     * @param procInsId
     * @return
     */
    public ProcessNode fetchNextNode(String procDefId,String currActId,String procInsId) {
        ProcessNode node = new ProcessNode();

        // 当前执行节点id
        ProcessDefinitionEntity dfe = (ProcessDefinitionEntity) ((RepositoryServiceImpl)repositoryService).getDeployedProcessDefinition(procDefId);
        // 获取所有节点
        List<ActivityImpl> activitiList = dfe.getActivities();
        // 判断出当前流程所处节点，根据路径获得下一个节点实例
        for(ActivityImpl activityImpl : activitiList){
            if (activityImpl.getId().equals(currActId)) {
                // 获取下一个节点
                List<PvmTransition> pvmTransitions = activityImpl.getOutgoingTransitions();

                PvmActivity pvmActivity = pvmTransitions.get(0).getDestination();

                String type = pvmActivity.getProperty("type").toString();
                if("userTask".equals(type)){
                    // 用户任务节点
                    node.setType(WorkflowConstants.NODE_TYPE_TASK);
                    node.setTitle(pvmActivity.getProperty("name").toString());
                    // 设置关联用户
                    List<LoginUser> users = getNodetUsers(pvmActivity.getId());
                    // TODO 去重用户？？？？？？？？？？？？？？？
                    node.setUsers(removeDuplicate(users));
                }else if("exclusiveGateway".equals(type)){
                    TaskDefinition taskDefinition = nextTaskDefinition(activityImpl, currActId, null, procInsId);

                    //设置用户节点的数据
                    // 用户任务节点
                    node.setTitle(taskDefinition.getNameExpression().getExpressionText());
                    // 设置关联用户
                    List<LoginUser> users = getNodetUsers(taskDefinition.getKey());
                    node.setUsers(removeDuplicate(users));
                    node.setType(WorkflowConstants.NODE_TYPE_TASK);
                }else if("parallelGateway".equals(type)){
                    // 平行网关
                    node.setType(WorkflowConstants.NODE_TYPE_PG);
                } else if ("scriptTask".equals(type)) {
                    //如果脚本任务在用户任务中间，就要解析脚本任务下一个用户任务的审批人
                    //脚本任务定义在流程的最后一端，直接出发执行
                } else if("endEvent".equals(type)){
                    // 结束
                    node.setType(WorkflowConstants.NODE_TYPE_END);
                }else{
                    throw new BpmnActivitiException("流程设计错误，包含无法处理的节点");
                }
                break;
            }
        }

        return node;
    }

    /**
     * 下一个任务节点信息,
     *
     * 如果下一个节点为用户任务则直接返回,
     *
     * 如果下一个节点为排他网关, 获取排他网关Id信息, 根据排他网关Id信息和execution获取流程实例排他网关Id为key的变量值,
     * 根据变量值分别执行排他网关后线路中的el表达式, 并找到el表达式通过的线路后的用户任务
     * @param  activityImpl     流程节点信息
     * @param  activityId             当前流程节点Id信息
     * @param  elString               排他网关顺序流线段判断条件
     * @param  processInstanceId      流程实例Id信息
     * @return
     */
    private TaskDefinition nextTaskDefinition(ActivityImpl activityImpl, String activityId, String elString, String processInstanceId){

        PvmActivity ac = null;

        Object s = null;

        // 如果遍历节点为用户任务并且节点不是当前节点信息
        if ("userTask".equals(activityImpl.getProperty("type")) && !activityId.equals(activityImpl.getId())) {
            // 获取该节点下一个节点信息
            TaskDefinition taskDefinition = ((UserTaskActivityBehavior) activityImpl.getActivityBehavior())
                    .getTaskDefinition();
            return taskDefinition;
        } else if("exclusiveGateway".equals(activityImpl.getProperty("type"))){// 当前节点为exclusiveGateway
            List<PvmTransition> outTransitions = activityImpl.getOutgoingTransitions();
            //outTransitionsTemp = ac.getOutgoingTransitions();

            // 如果网关路线判断条件为空信息
            //  if (StringUtils.isEmpty(elString)) {
            // 获取流程启动时设置的网关判断条件信息
            elString = getGatewayCondition(activityImpl.getId(), processInstanceId);
            // }

            // 如果排他网关只有一条线路信息

            if (outTransitions.size() == 1) {
                return nextTaskDefinition((ActivityImpl) outTransitions.get(0).getDestination(), activityId,
                        elString, processInstanceId);
            } else if (outTransitions.size() > 1) { // 如果排他网关有多条线路信息
                for (PvmTransition tr1 : outTransitions) {
                    s = tr1.getProperty("conditionText"); // 获取排他网关线路判断条件信息
                    // 判断el表达式是否成立
                    if (isCondition(activityImpl.getId(), StrUtil.trim(s.toString()), elString)) {
                        return nextTaskDefinition((ActivityImpl) tr1.getDestination(), activityId, elString,
                                processInstanceId);
                    }

                }
            }
        }else {
            // 获取节点所有流向线路信息
            List<PvmTransition> outTransitions = activityImpl.getOutgoingTransitions();
            List<PvmTransition> outTransitionsTemp = null;
            for (PvmTransition tr : outTransitions) {
                ac = tr.getDestination(); // 获取线路的终点节点
                // 如果流向线路为排他网关
                if ("exclusiveGateway".equals(ac.getProperty("type"))) {
                    outTransitionsTemp = ac.getOutgoingTransitions();

                    // 如果网关路线判断条件为空信息
                    if (StrUtil.isEmpty(elString)) {
                        // 获取流程启动时设置的网关判断条件信息
                        elString = getGatewayCondition(ac.getId(), processInstanceId);
                    }

                    // 如果排他网关只有一条线路信息
                    if (outTransitionsTemp.size() == 1) {
                        return nextTaskDefinition((ActivityImpl) outTransitionsTemp.get(0).getDestination(), activityId,
                                elString, processInstanceId);
                    } else if (outTransitionsTemp.size() > 1) { // 如果排他网关有多条线路信息
                        for (PvmTransition tr1 : outTransitionsTemp) {
                            s = tr1.getProperty("conditionText"); // 获取排他网关线路判断条件信息
                            // 判断el表达式是否成立
                            if (isCondition(ac.getId(), StrUtil.trim(s.toString()), elString)) {
                                return nextTaskDefinition((ActivityImpl) tr1.getDestination(), activityId, elString,
                                        processInstanceId);
                            }
                        }
                    }
                } else if ("userTask".equals(ac.getProperty("type"))) {
                    return ((UserTaskActivityBehavior) ((ActivityImpl) ac).getActivityBehavior()).getTaskDefinition();
                } else {
                }
            }
            return null;
        }
        return null;
    }

    /**
     * 查询流程启动时设置排他网关判断条件信息
     * @param  gatewayId          排他网关Id信息, 流程启动时设置网关路线判断条件key为网关Id信息
     * @param  processInstanceId  流程实例Id信息
     * @return
     */
    public String getGatewayCondition(String gatewayId, String processInstanceId) {
        Execution execution = runtimeService.createExecutionQuery().processInstanceId(processInstanceId).singleResult();
        Object object= runtimeService.getVariable(execution.getId(), gatewayId);
        return object==null? "":object.toString();
    }

    /**
     * 根据key和value判断el表达式是否通过信息
     * @param  key    el表达式key信息
     * @param  el     el表达式信息
     * @param  value  el表达式传入值信息
     * @return
     */
    public boolean isCondition(String key, String el, String value) {
        ExpressionFactory factory = new ExpressionFactoryImpl();
        SimpleContext context = new SimpleContext();
        context.setVariable(key, factory.createValueExpression(value, String.class));
        ValueExpression e = factory.createValueExpression(context, el, boolean.class);
        boolean res = (Boolean) e.getValue(context);
        return res;
    }

    /**
     * 根据tableId和tableName查询表达式key和value
     * @param  tableId    el表达式key信息
     * @param  el     el表达式信息
     * @param  tableName  el表达式传入值信息
     * @return
     */
    public boolean isElCondition(String tableId, String el, String tableName) {
        ExpressionFactory factory = new ExpressionFactoryImpl();
        SimpleContext context = new SimpleContext();
        Map<String, Object> elMap = zBusinessService.getApplyForm(tableId, tableName);
        if(!elMap.isEmpty()){
            for(String key:elMap.keySet()){
                if(el.indexOf(key) != -1){
                    context.setVariable(key, factory.createValueExpression(elMap.get(key), String.class));
                }
            }
        }
        ValueExpression e = factory.createValueExpression(context, el, boolean.class);
        boolean res = (Boolean) e.getValue(context);
        return res;
    }


    /**
     * 设置节点审批人
     * @param nodeId
     */
    public List<LoginUser> getNodetUsers(String nodeId){
        List<LoginUser> users = zNodeService.findUserByNodeId(nodeId);
        // 设置关联角色的用户
        List<ActivitiRole> roles = zNodeService.findRoleByNodeId(nodeId);
        for(ActivitiRole r : roles){
            List<LoginUser> userList = zNodeService.findUserByRoleId(r.getRoleId());
            users.addAll(userList);
        }
        // 设置关联部门负责人
        List<ActivitiDepartment> departments = zNodeService.findDepartmentByNodeId(nodeId);
        for (ActivitiDepartment d : departments){
            List<LoginUser> userList = zNodeService.findUserDepartmentId(d.getId());
            users.addAll(userList);
        }
        // 判断获取部门负责人
        if(zNodeService.hasChooseDepHeader(nodeId)){
            List<LoginUser> allUser = zNodeService.queryAllUser();
            LoginUser loginUser = LoginContext.me().getLoginUser();
            List<String> departIds = zNodeService.getOrgIdsByAccount(loginUser.getAccount());
            for (String departId : departIds) {
                List<LoginUser> collect = allUser.stream().filter(u -> u.getOrganizationId() != null && String.valueOf(u.getOrganizationId()).indexOf(departId) > -1).collect(Collectors.toList());
                users.addAll(collect);
            }
        }
        // 判断获取发起人
        if(zNodeService.hasChooseSponsor(nodeId)){
            LoginUser loginUser = LoginContext.me().getLoginUser();
            users.add(loginUser);
        }
        // TODO 过滤删除的用户[删除的用户]
        // users = users.stream().filter(u->StrUtil.equals("0",u.getDelFlag()+"")).collect(Collectors.toList());
        return users;
    }

    /***
     * 通过节点nodeId获取审批人
     * @param nodeId 节点id
     * @return
     */
    public ProcessNode fetchNode(String nodeId) {
        ProcessNode node = new ProcessNode();
        // 设置关联用户
        List<LoginUser> users = getNodetUsers(nodeId);
        node.setUsers(removeDuplicate(users));
        return node;
    }

    /**
     * 去重
     * @param list
     * @return
     */
    private List<LoginUser> removeDuplicate(List<LoginUser> list) {

        LinkedHashSet<LoginUser> set = new LinkedHashSet<>(list.size());
        set.addAll(list);
        list.clear();
        list.addAll(set);
        return list;
    }

    //////////////////////////////////////流程定义-结束/////////////////////////////////////////////////


    //////////////////////////////////////流程业务-开始/////////////////////////////////////////////////


    /***
     * 添加申请草稿状态
     * @param tableId
     * @param paramsMap
     * @param type
     */
    public void addDraft(String tableId, Map<String, Object> paramsMap, Integer type) {

        String procDefId = paramsMap.get("procDefId")+"";
        String procDeTitle = paramsMap.get("procDeTitle")+"";
        String appId = paramsMap.get("appId")+"";
        String tableName = paramsMap.get("tableName")+"";
        this.saveApplyForm(tableId,paramsMap);
        // 保存至我的申请业务
        LoginUser sysUser = LoginContext.me().getLoginUser();
        String username = sysUser.getAccount();
        ActivitiZBusiness actBusiness = new ActivitiZBusiness();
        actBusiness.setUserId(username);
        actBusiness.setTableId(tableId);
        actBusiness.setProcDefId(procDefId);
        String title = StrUtil.isEmptyIfStr(paramsMap.get(WorkflowConstants.titleKey))?"":paramsMap.get(WorkflowConstants.titleKey)+"";
        if (StrUtil.isNotBlank(title)){
            actBusiness.setTitle(title);
        }else {
            actBusiness.setTitle(procDeTitle);
        }

        //关联所属应用
        if (StrUtil.isNotBlank(appId)) {
            actBusiness.setAppid(appId);
        }

        actBusiness.setTableName(tableName);
        actBusiness.setType(type);
        zBusinessService.save(actBusiness);
    }

    /***
     * 保存|修改 业务表单数据到数据库表[该方法相对通用，复杂业务单独定制，套路类似]
     * @param tableId 业务表中的数据id
     * @param paramsMap
     */
    public void saveApplyForm(String tableId, Map<String,Object> paramsMap) {
        zBusinessService.saveApplyForm(tableId,paramsMap);
    }

    /**
     * 获取业务表单数据
     * @param tableId
     * @param tableName
     * @return
     */
    public Map<String, Object> fetchApplyForm(String tableId, String tableName) {
        return zBusinessService.getApplyForm(tableId,tableName);
    }


    /***
     * 通过id删除草稿状态申请
     * @param ids
     * @return
     */
    public boolean removeDraftByIds(String ids) {

        for(String id : ids.split(",")){
            ActivitiZBusiness actBusiness = zBusinessService.getById(id);
            if(actBusiness.getStatus()!=WorkflowConstants.STATUS_TO_APPLY){
                log.info("删除失败, 仅能删除草稿状态的申请");
                return false;
            }
            // 删除关联业务表
            zBusinessService.deleteBusiness(actBusiness.getTableName(), actBusiness.getTableId());
            zBusinessService.removeById(id);
        }
        return true;
    }

    /***
     * 提交申请 启动流程
     * @param business
     */
    public void apply(ActivitiZBusiness business,BpmnUser bpmnUser) {
        ActivitiZBusiness actBusiness = zBusinessService.getById(business.getId());
        if (ObjectUtil.isEmpty(actBusiness)) {
            log.info("actBusiness表中该id不存在");
            throw new RuntimeException("actBusiness表中该id不存在");
        }
        String tableId = actBusiness.getTableId();
        String tableName = actBusiness.getTableName();
        business.setTableId(tableId);
        Map<String, Object> busiData = zBusinessService.getBaseMapper().getBusiData(tableId, tableName);

        if (MapUtil.isNotEmpty(busiData)&&busiData.get(WorkflowConstants.titleKey)!=null){
            //如果表单里有 标题  更新一下
            actBusiness.setTitle(busiData.get(WorkflowConstants.titleKey)+"");
        }
        String processInstanceId = startProcess(business,bpmnUser);
        actBusiness.setProcInstId(processInstanceId);
        actBusiness.setStatus(WorkflowConstants.STATUS_DEALING);
        actBusiness.setResult(WorkflowConstants.RESULT_DEALING);
        actBusiness.setApplyTime(new Date());
        zBusinessService.updateById(actBusiness);
        //修改业务表的流程字段
        zBusinessService.updateBusinessStatus(actBusiness.getTableName(), actBusiness.getTableId(),"启动");

    }

    /***
     * 启动流程
     * @param actBusiness
     * @return
     */
    public String startProcess(ActivitiZBusiness actBusiness,BpmnUser bpmnUser) {




        // 启动流程用户
        identityService.setAuthenticatedUserId(bpmnUser.getAccount());
        // 启动流程 需传入业务表id变量
        Map<String, Object> params = actBusiness.getParams();
        params.put("tableId", actBusiness.getTableId());
        ActivitiZBusiness act = zBusinessService.getById(actBusiness.getId());
        String tableName = act.getTableName();
        String tableId = act.getTableId();
        if (StrUtil.isBlank(tableId)||StrUtil.isBlank(tableName)){
            throw new BpmnActivitiException("没有业务表单数据");

        }
        /*表单数据写入*/
        Map<String, Object> busiData = zBusinessService.getBaseMapper().getBusiData(tableId, tableName);
        for (String key : busiData.keySet()) {
            params.put(key,busiData.get(key));
        }
        // 启动流程实例把会签的参数都赋值上去。nrOfInstances nrOfActiveInstances nrOfCompletedInstances loopCounter
        String s = actBusiness.getAssignees();
        String [] arr = s.split(",");
        params.put("spUserList",Arrays.asList(arr));
        params.put("spUser",Arrays.asList(arr));
        ProcessInstance pi = runtimeService.startProcessInstanceById(actBusiness.getProcDefId(), actBusiness.getId(), params);
        // 设置流程实例名称
        runtimeService.setProcessInstanceName(pi.getId(), actBusiness.getTitle());
        List<Task> tasks = taskService.createTaskQuery().processInstanceId(pi.getId()).list();

        Task first = null;
        if (tasks.size() > 0) {
            first = tasks.get(0);
        }
        // 判断多实例还是单实例
        boolean singleIns = false;
        String executionId = first.getExecutionId();
        String processInstanceId = first.getProcessInstanceId();
        // 判断流程图中是否包含并行网关,并行网关中的assignee这个字段没有值，因此走之前逻辑
        String assigneeVal = first.getAssignee();
        if (executionId.equals(processInstanceId)) {
            singleIns = true;
        }

        if (singleIns &&(StrUtil.isEmpty(assigneeVal))) {
            for(Task task : tasks){
                if(StrUtil.isEmpty(task.getName())){
                    task.setName("");
                }
                if(actBusiness.getFirstGateway()){
                    // 网关类型
                    List<LoginUser> users = fetchNode(task.getTaskDefinitionKey()).getUsers();
                    // 如果下个节点未分配审批人为空 取消结束流程
                    if(users==null||users.size()==0){
                        throw new RuntimeException("任务节点未分配任何候选审批人，发起流程失败");
                    }else{
                        // 分配了节点负责人分发给全部
                        for(LoginUser user : users){
                            taskService.addCandidateUser(task.getId(), user.getAccount());
                            BpmnUser toUser = zNodeService.getUserByAccount(user.getAccount());
                            sendActMessage(bpmnUser,toUser,actBusiness,task.getName(), actBusiness.getSendMessage(),
                                    actBusiness.getSendSms(), actBusiness.getSendEmail());
                        }
                    }
                }else {
                    // 分配第一个任务用户
                    String assignees = actBusiness.getAssignees();
                    for (String assignee : assignees.split(",")) {
                        taskService.addCandidateUser(task.getId(), assignee);
                        // 根据账户account获取用户信息
                        BpmnUser toUser = zNodeService.getUserByAccount(assignee);
                        sendActMessage(bpmnUser,toUser,actBusiness,task.getName(), actBusiness.getSendMessage(),
                                actBusiness.getSendSms(), actBusiness.getSendEmail());
                    }
                }
                // 设置任务优先级
                taskService.setPriority(task.getId(), actBusiness.getPriority());
            }
        } else {
            for(Task task : tasks){

                if(StrUtil.isEmpty(task.getName())){
                    task.setName("");
                }
                if(actBusiness.getFirstGateway()){
                    // 网关类型
                    List<LoginUser> users = fetchNode(task.getTaskDefinitionKey()).getUsers();
                    // 如果下个节点未分配审批人为空 取消结束流程
                    if(users==null||users.size()==0){
                        throw new RuntimeException("任务节点未分配任何候选审批人，发起流程失败");
                    }else{
                        // 分配了节点负责人分发给全部
                        for(LoginUser user : users){
                            taskService.addCandidateUser(task.getId(), user.getAccount());
                            BpmnUser toUser = zNodeService.getUserByAccount(user.getAccount());
                            sendActMessage(bpmnUser,toUser,actBusiness,task.getName(), actBusiness.getSendMessage(),
                                    actBusiness.getSendSms(), actBusiness.getSendEmail());
                        }
                    }
                }else {
                    // 分配第一个任务用户
                    String assignees = actBusiness.getAssignees();
                    for (String assignee : assignees.split(",")) {
                        taskService.addCandidateUser(task.getId(), assignee);
                        // 根据账户account获取用户信息
                        BpmnUser toUser = zNodeService.getUserByAccount(assignee);
                        sendActMessage(bpmnUser,toUser,actBusiness,task.getName(), actBusiness.getSendMessage(),
                                actBusiness.getSendSms(), actBusiness.getSendEmail());
                    }
                }
                // 设置任务优先级
                taskService.setPriority(task.getId(), actBusiness.getPriority());
                // 多实例会签，任务节点是一个，实例是多个，因此循环一次，否则消息发送和落库会重
                break;
            }
        }






        return pi.getId();
    }


    /**
     * 发送流程信息
     * @param fromUser 发送人
     * @param toUser 接收人
     * @param act 流程
     * @param taskName
     * @param sendMessage 系统消息
     * @param sendSms 短信消息
     * @param sendEmail 邮件消息
     */
    public void sendActMessage(BpmnUser fromUser, BpmnUser toUser, ActivitiZBusiness act, String taskName, Boolean sendMessage, Boolean sendSms, Boolean sendEmail) {
        MessageApi messageApi = MessageContext.me("DingDingMessageImpl");
        if (ObjectUtil.isNull(messageApi)) {
            String title = String.format("您有一个新的审批任务");
            Map<String, String> msgMap = new HashMap<>();
            msgMap.put("bpm_name",act.getTitle());
            msgMap.put("bpm_task",taskName);
            msgMap.put("datetime", DateUtil.now());
            msgMap.put("remark", "请进入待办栏，尽快处理！");
            // 流程催办模板
            NewsTemplate newsTemplate = new NewsTemplate();
            newsTemplate.setTemplateCode("bpm_cuiban");
            newsTemplate.setTemplateParam(msgMap);
            String msgText = parseTemplateByCode(newsTemplate);
            this.sendMessage(fromUser,toUser,title,msgText,sendMessage,sendSms,sendEmail);
        } else {
            String title = String.format("您有一个新的审批任务");
            Map<String, String> msgMap = new HashMap<>();
            msgMap.put("bpm_name",act.getTitle());
            msgMap.put("bpm_task",taskName);
            msgMap.put("datetime", DateUtil.now());
            msgMap.put("remark", "请进入待办栏，尽快处理！");
            // 流程催办模板
            NewsTemplate newsTemplate = new NewsTemplate();
            newsTemplate.setTemplateCode("bpm_cuiban_dingding");
            newsTemplate.setTemplateParam(msgMap);
            String msgText = parseTemplateByCode(newsTemplate);
            this.sendMessage(fromUser,toUser,title,msgText,sendMessage,sendSms,sendEmail);
        }


    }

    /**
     * 发消息
     * @param fromUser 发送人
     * @param toUser 接收人
     * @param title 标题
     * @param msgText 信息内容
     * @param sendMessage 系统消息
     * @param sendSms 短信
     * @param sendEmail 邮件
     */
    @Override
    public void sendMessage(BpmnUser fromUser, BpmnUser toUser, String title, String msgText, Boolean sendMessage, Boolean sendSms, Boolean sendEmail) {

        MessageApi messageApi = MessageContext.me("DingDingMessageImpl");
        if (ObjectUtil.isNull(messageApi)) {
            if (sendMessage!=null&&sendMessage){
                // TODO 优化点，发送可以搞成模板
                MessageSendRequest messageSendRequest = new MessageSendRequest();
                messageSendRequest.setReceiveUserIds(toUser.getUserId()+"");
                messageSendRequest.setMessageTitle(title);
                messageSendRequest.setMessageContent(msgText);
                MessageContext.me("MessageDbServiceImpl").sendMessage(messageSendRequest);
            }
            //todo 以下需要购买阿里短信服务；设定邮件服务账号
            if (sendSms!=null&&sendSms&& StrUtil.isNotBlank(toUser.getPhone())){
                // 参数说明：phone 手机号    templateCode 短信模板    params填充模板的数据
                SmsContext.me().sendSms("","",null);
            }
            // 发送邮件
            if (sendEmail!=null&&sendEmail&& StrUtil.isNotBlank(toUser.getEmail())){
                SendMailParam sendMailParam = new SendMailParam();
                // 设置邮件内容
                sendMailParam.setContent(msgText);
                // 设置邮件标题
                sendMailParam.setTitle(title);

                // 收件人列表
                List<String> tos = new ArrayList<>();
                tos.add(toUser.getEmail());
                sendMailParam.setTos(tos);
                // 发送普通邮件
                MailSenderContext.me().sendMail(sendMailParam);
            }
        } else {
            // 默认都是走钉钉
            MessageSendRequest messageSendRequest = new MessageSendRequest();
            messageSendRequest.setReceiveUserIds(toUser.getUserId()+"");
            messageSendRequest.setMessageTitle(title);
            messageSendRequest.setMessageContent(msgText);
            MessageContext.me("DingDingMessageImpl").sendMessage(messageSendRequest);
        }
    }


    /***
     * 撤回申请
     * @param id
     * @param procInstId
     * @param reason
     */
    public void cancel( String id, String procInstId, String reason) {
        if(StrUtil.isBlank(reason)){
            reason = "";
        }
        runtimeService.deleteProcessInstance(procInstId, "canceled-"+reason);
        ActivitiZBusiness actBusiness = zBusinessService.getById(id);
        actBusiness.setStatus(WorkflowConstants.STATUS_CANCELED);
        actBusiness.setResult(WorkflowConstants.RESULT_TO_SUBMIT);
        zBusinessService.updateById(actBusiness);
        //修改业务表的流程字段
        zBusinessService.updateBusinessStatus(actBusiness.getTableName(), actBusiness.getTableId(),"撤回");
    }

    /***
     * 获取流程业务数据
     * @return
     */
    public PageResult<ActivitiZBusiness> fetchActZBusinessList(ActivitiZBusiness param, Map<String,Object> paramMap) {
        //所属应用id
        String appId = String.valueOf(paramMap.get("appId"));

        LambdaQueryWrapper<ActivitiZBusiness> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.orderByDesc(ActivitiZBusiness::getCreateTime);
        if (StrUtil.isNotBlank(param.getTitle())) queryWrapper.like(ActivitiZBusiness::getTitle,param.getTitle());
        if (param.getResult()!=null) queryWrapper.eq(ActivitiZBusiness::getResult,param.getResult());
        if (StrUtil.isNotBlank(paramMap.get("createTime_begin")+"")) queryWrapper.ge(ActivitiZBusiness::getTitle,param.getTitle());
        if (StrUtil.isNotBlank(paramMap.get("createTime_end")+"")) queryWrapper.le(ActivitiZBusiness::getTitle,param.getTitle());
        if (param.getStatus()!=null){
            queryWrapper.eq(ActivitiZBusiness::getStatus,param.getStatus());
        }else {
            queryWrapper.gt(ActivitiZBusiness::getStatus,0);
        }
        //查询设置appid
        if (StrUtil.isNotBlank(appId)) queryWrapper.eq(ActivitiZBusiness::getAppid,appId);

        // 当前登录用户
        LoginUser loginUser = LoginContext.me().getLoginUser();
        queryWrapper.eq(ActivitiZBusiness::getUserId,loginUser.getAccount());
        Page<ActivitiZBusiness> actBusinessList = zBusinessService.page(PageFactory.defaultPage(), queryWrapper);

        actBusinessList.getRecords().forEach(e -> {
            if(StrUtil.isNotBlank(e.getProcDefId())){
                ActivitiZProcess actProcess = zProcessService.getById(e.getProcDefId());
                e.setRouteName(actProcess.getRouteName());
                e.setProcessName(actProcess.getName());
            }
            if(WorkflowConstants.STATUS_DEALING.equals(e.getStatus())){
                // 关联当前任务
                List<Task> taskList = taskService.createTaskQuery().processInstanceId(e.getProcInstId()).list();
                if(taskList!=null&&taskList.size()==1){
                    e.setCurrTaskName(taskList.get(0).getName());
                }else if(taskList!=null&&taskList.size()>1){
                    StringBuilder sb = new StringBuilder();
                    for(int i=0;i<taskList.size()-1;i++){
                        sb.append(taskList.get(i).getName()+"、");
                    }
                    sb.append(taskList.get(taskList.size()-1).getName());
                    e.setCurrTaskName(sb.toString());
                }
            }
        });
        return PageResultFactory.createPageResult(actBusinessList);
    }
    //////////////////////////////////////流程业务-结束/////////////////////////////////////////////////

    //////////////////////////////////////流程实例-开始/////////////////////////////////////////////////


    /***
     * 获取运行中的流程实例
     * @param name
     * @param categoryId
     * @param key
     * @param allUser
     * @return
     */
    public List<RunProcessInsResponse> getRunningProcess(String name,String categoryId,String key,List<ComboModel> allUser) {
        List<RunProcessInsResponse> list = new ArrayList<>();

        ProcessInstanceQuery query = runtimeService.createProcessInstanceQuery()
                .orderByProcessInstanceId().desc();

        if(StrUtil.isNotBlank(name)){
            query.processInstanceNameLike("%"+name+"%");
        }
        if(StrUtil.isNotBlank(categoryId)){
            query.processDefinitionCategory(categoryId);
        }
        if(StrUtil.isNotBlank(key)) {
            query.processDefinitionKey(key);
        }

        List<ProcessInstance> processInstanceList = query.list();
        processInstanceList.forEach(e -> {
            list.add(new RunProcessInsResponse(e));
        });
        Map<String, String> userMap = allUser.stream().collect(Collectors.toMap(ComboModel::getUsername, ComboModel::getTitle));
        list.forEach(e -> {
            List<HistoricIdentityLink> identityLinks = historyService.getHistoricIdentityLinksForProcessInstance(e.getId());
            for(HistoricIdentityLink hik : identityLinks){
                // 关联发起人
                if("starter".equals(hik.getType())&& StrUtil.isNotBlank(hik.getUserId())){
                    e.setApplyer(userMap.get(hik.getUserId()));
                }
            }
            // 关联当前任务
            List<Task> taskList = taskService.createTaskQuery().processInstanceId(e.getProcInstId()).list();
            if(taskList!=null&&taskList.size()==1){
                e.setCurrTaskName(taskList.get(0).getName());
            }else if(taskList!=null&&taskList.size()>1){
                StringBuilder sb = new StringBuilder();
                for(int i=0;i<taskList.size()-1;i++){
                    sb.append(taskList.get(i).getName()+"、");
                }
                sb.append(taskList.get(taskList.size()-1).getName());
                e.setCurrTaskName(sb.toString());
            }
            // 关联流程表单路由
            ActivitiZProcess actProcess = zProcessService.getById(e.getProcDefId());
            if(actProcess!=null){
                e.setRouteName(actProcess.getRouteName());
            }
            // 关联业务表id
            ActivitiZBusiness actBusiness = zBusinessService.getById(e.getBusinessKey());
            if(actBusiness!=null){
                e.setTableId(actBusiness.getTableId());
                e.setTableName(actBusiness.getTableName());
            }
        });
        return list;
    }


    /***
     * 通过id删除运行中的实例
     * @param ids 逗号分割
     * @param reason 删除原因
     */
    public void delInsByIds(String ids,String reason) {
        if(StrUtil.isBlank(reason)){
            reason = "";
        }
        for(String id : ids.split(",")){
            // 关联业务状态结束
            ProcessInstance pi = runtimeService.createProcessInstanceQuery().processInstanceId(id).singleResult();
            ActivitiZBusiness actBusiness = zBusinessService.getById(pi.getBusinessKey());
            actBusiness.setStatus(WorkflowConstants.STATUS_TO_APPLY);
            actBusiness.setResult(WorkflowConstants.RESULT_TO_SUBMIT);
            zBusinessService.updateById(actBusiness);
            runtimeService.deleteProcessInstance(id, WorkflowConstants.DELETE_PRE+reason);
        }
    }

    /***
     * 激活或挂起流程实例
     * @param id 流程实例id
     * @param status 状态
     */
    public void updateStatus(String id,Integer status) {
        if(WorkflowConstants.PROCESS_STATUS_ACTIVE.equals(status)){
            runtimeService.activateProcessInstanceById(id);
        }else if(WorkflowConstants.PROCESS_STATUS_SUSPEND.equals(status)){
            runtimeService.suspendProcessInstanceById(id);
        }
    }

    /***
     * 获取结束的的流程实例
     * @param name
     * @param categoryId
     * @param key
     * @param startDate
     * @param endDate
     * @param allUser
     * @return
     */
    public List<HistoricProcessInsResponse> getFinishedProcess(String name,
            String categoryId,
            String key,
            String startDate,
            String endDate,List<ComboModel> allUser) {

        List<HistoricProcessInsResponse> list = new ArrayList<>();

        HistoricProcessInstanceQuery query = historyService.createHistoricProcessInstanceQuery().finished().
                orderByProcessInstanceEndTime().desc();
        if(StrUtil.isNotBlank(name)){
            query.processInstanceNameLike("%"+name+"%");
        }
        if(StrUtil.isNotBlank(categoryId)){
            query.processDefinitionCategory(categoryId);
        }
        if(StrUtil.isNotBlank(key)) {
            query.processDefinitionKey(key);
        }

        if(StrUtil.isNotBlank(startDate)&&StrUtil.isNotBlank(endDate)){
            Date start = DateUtil.parse(startDate);
            Date end = DateUtil.parse(endDate);
            query.finishedAfter(start);
            query.finishedBefore(DateUtil.endOfDay(end));
        }

        List<HistoricProcessInstance> processInstanceList = query.list();
        processInstanceList.forEach(e -> {
            list.add(new HistoricProcessInsResponse(e));
        });

        // 查询所有的用户列表信息

        Map<String, String> userMap = allUser.stream().collect(Collectors.toMap(ComboModel::getUsername, ComboModel::getTitle));

        list.forEach(e -> {
            List<HistoricIdentityLink> identityLinks = historyService.getHistoricIdentityLinksForProcessInstance(e.getId());
            for(HistoricIdentityLink hik : identityLinks){
                // 关联发起人
                if("starter".equals(hik.getType())&&StrUtil.isNotBlank(hik.getUserId())){
                    e.setApplyer(userMap.get(hik.getUserId()));
                }
            }
            // 关联流程表单路由
            ActivitiZProcess actProcess = zProcessService.getById(e.getProcDefId());
            if(actProcess!=null){
                e.setRouteName(actProcess.getRouteName());
            }
            // 关联业务表id和结果
            ActivitiZBusiness actBusiness = zBusinessService.getById(e.getBusinessKey());
            if(actBusiness!=null){
                e.setTableId(actBusiness.getTableId());
                e.setTableName(actBusiness.getTableName());
                String reason = e.getDeleteReason();
                if(reason==null){
                    e.setResult(WorkflowConstants.RESULT_PASS);
                }else if(reason.contains(WorkflowConstants.CANCEL_PRE)){
                    e.setResult(WorkflowConstants.RESULT_CANCEL);
                    if(reason.length()>9){
                        e.setDeleteReason(reason.substring(9));
                    }else{
                        e.setDeleteReason("");
                    }
                }else if(WorkflowConstants.BACKED_FLAG.equals(reason)){
                    e.setResult(WorkflowConstants.RESULT_FAIL);
                    e.setDeleteReason("");
                }else if(reason.contains(WorkflowConstants.DELETE_PRE)){
                    e.setResult(WorkflowConstants.RESULT_DELETED);
                    if(reason.length()>8){
                        e.setDeleteReason(reason.substring(8));
                    }else{
                        e.setDeleteReason("");
                    }
                }else{
                    e.setResult(WorkflowConstants.RESULT_PASS);
                }
            }
        });

        return  list;

    }




    /***
     * 通过id删除已结束的实例
     * @param ids 逗号分割
     */
    public void delHistoricInsByIds(String ids) {
        for(String id : ids.split(",")){
            historyService.deleteHistoricProcessInstance(id);
        }
    }


    //////////////////////////////////////流程实例-结束/////////////////////////////////////////////////



    //////////////////////////////////////流程任务-开始/////////////////////////////////////////////////


    public PageResult<ProcessTaskResponse> todoList(String name,
                                                    String categoryId,
                                                    Integer priority,
                                                    BpmnUser bpmnUser,
                                                    String createTimeBegin,
                                                    String createTimeEnd, int firstResult, int maxResults) {
        List<ProcessTaskResponse> list = new ArrayList<>();

        String userId = bpmnUser.getAccount();
        TaskQuery query = taskService.createTaskQuery().taskCandidateOrAssigned(userId);
        // 多条件搜索
        query.orderByTaskPriority().desc();
        query.orderByTaskCreateTime().desc();
        if(StrUtil.isNotBlank(name)){
            query.taskNameLike("%"+name+"%");
        }
        if(StrUtil.isNotBlank(categoryId)){
            query.taskCategory(categoryId);
        }
        if(priority!=null){
            query.taskPriority(priority);
        }

        if(StrUtil.isNotBlank(createTimeBegin)&&StrUtil.isNotBlank(createTimeEnd)){
            Date start = DateUtil.parse(createTimeBegin);
            Date end = DateUtil.parse(createTimeEnd);
            query.taskCreatedAfter(start);
            query.taskCreatedBefore(DateUtil.endOfDay(end));
        }

        List<Task> taskList = query.listPage((firstResult-1)*maxResults,maxResults);

        // 转换vo
        taskList.forEach(e -> {
            ProcessTaskResponse tv = new ProcessTaskResponse(e);

            // 关联委托人
            if(StrUtil.isNotBlank(tv.getOwner())){
//                String account = zNodeService.getUserByAccount(tv.getOwner()).getAccount();
                tv.setOwner(tv.getOwner());
            }
            List<IdentityLink> identityLinks = runtimeService.getIdentityLinksForProcessInstance(tv.getProcInstId());
            for(IdentityLink ik : identityLinks){
                // 关联发起人
                if("starter".equals(ik.getType())&&StrUtil.isNotBlank(ik.getUserId())){
                    tv.setApplyer(ik.getUserId());
                }
            }
            // 关联流程信息
            ActivitiZProcess actProcess = zProcessService.getById(tv.getProcDefId());
            if(actProcess!=null){
                tv.setProcessName(actProcess.getName());
                tv.setRouteName(actProcess.getRouteName());
                tv.setType(actProcess.getType());
            }
            // 关联业务key
            ProcessInstance pi = runtimeService.createProcessInstanceQuery().processInstanceId(tv.getProcInstId()).singleResult();
            tv.setBusinessKey(pi.getBusinessKey());
            ActivitiZBusiness actBusiness = zBusinessService.getById(pi.getBusinessKey());
            if(actBusiness!=null){
                tv.setTableId(actBusiness.getTableId());
                tv.setTableName(actBusiness.getTableName());
            }

            list.add(tv);
        });

        Page<ProcessTaskResponse> page = new Page<>();
        page.setRecords(list);
        page.setTotal(query.count());
        return PageResultFactory.createPageResult(page);
    }



    /***
     * 获取可返回的节点
     * @param procInstId 流程实例id
     * @return
     */
    public List<HistoricTaskResponse> getBackList(String procInstId) {
        List<HistoricTaskResponse> list = new ArrayList<>();
        List<HistoricTaskInstance> taskInstanceList = historyService.createHistoricTaskInstanceQuery().processInstanceId(procInstId)
                .finished().list();




        // 转换vo
        taskInstanceList.forEach(e -> {
            HistoricTaskResponse htv = new HistoricTaskResponse(e);
            List<Assignee> assignees = new ArrayList<>();
            //获取节点审批人
            List<HistoricIdentityLink> identityLinks = historyService.getHistoricIdentityLinksForTask(e.getId());
            // 获取实际审批用户id
            String userId = zBusinessService.findUserIdByTypeAndTaskId(WorkflowConstants.EXECUTOR_TYPE, e.getId());
            for(HistoricIdentityLink hik : identityLinks){
                // 关联候选用户（分配的候选用户审批人）
                if("candidate".equals(hik.getType())&& StrUtil.isNotBlank(hik.getUserId())){
                    String username = zNodeService.getUserByAccount(hik.getUserId()).getAccount();
                    Assignee assignee = new Assignee(username, false);
                    if(StrUtil.isNotBlank(userId)&&userId.equals(hik.getUserId())){
                        assignee.setIsExecutor(true);
                        assignees.add(assignee);
                    }
                }
            }
            htv.setAssignees(assignees);
            list.add(htv);
        });

        // 去重
        LinkedHashSet<String> set = new LinkedHashSet<String>(list.size());
        List<HistoricTaskResponse> newList = new ArrayList<>();
        list.forEach(e->{
            if(set.add(e.getName())){
                newList.add(e);
            }
        });
        return newList;
    }
    /***
     * 任务节点审批 驳回至发起人
     * @param id 任务id
     * @param procInstId 流程实例id
     * @param comment 意见评论
     * @param sendMessage 是否发送站内消息
     * @param sendSms 是否发送短信通知
     * @param sendEmail 是否发送邮件通知
     */
    public void back( String id,
                      String procInstId,
                      String comment,
                      Boolean sendMessage,
                      Boolean sendSms,
                      Boolean sendEmail,BpmnUser fromUser) {

        if(StrUtil.isBlank(comment)){
            comment = "";
        }
        taskService.addComment(id, procInstId, comment);
        ProcessInstance pi = runtimeService.createProcessInstanceQuery().processInstanceId(procInstId).singleResult();
        // 删除流程实例
        runtimeService.deleteProcessInstance(procInstId, "backed");
        ActivitiZBusiness actBusiness = zBusinessService.getById(pi.getBusinessKey());
        actBusiness.setStatus(WorkflowConstants.STATUS_FINISH);
        actBusiness.setResult(WorkflowConstants.RESULT_FAIL);
        zBusinessService.updateById(actBusiness);
        // 异步发消息
        BpmnUser toUser = zNodeService.getUserByAccount(actBusiness.getUserId());
        sendMessage(fromUser,toUser,WorkflowConstants.MESSAGE_BACK_CONTENT,
                String.format("您的 【%s】 申请已被驳回！",actBusiness.getTitle()),sendMessage, sendSms, sendEmail);
        // 记录实际审批人员
        zBusinessService.insertHiIdentityLink(IdUtil.simpleUUID(),
                WorkflowConstants.EXECUTOR_TYPE, fromUser.getAccount(), id, procInstId);
        //修改业务表的流程字段
        zBusinessService.updateBusinessStatus(actBusiness.getTableName(), actBusiness.getTableId(),"驳回");

    }


    class TaskIdAndAssingeModel {
        String taskId;
        String assigne;

        public TaskIdAndAssingeModel(String taskId,String assigne) {
            this.taskId = taskId;
            this.assigne = assigne;
        }

        public String getTaskId() {
            return taskId;
        }

        public void setTaskId(String taskId) {
            this.taskId = taskId;
        }

        public String getAssigne() {
            return assigne;
        }

        public void setAssigne(String assigne) {
            this.assigne = assigne;
        }
    }

    /***
     * 流程流转历史
     * @param id 流程实例id
     * @return
     */
    public List<HistoricTaskResponse> historicFlow(String id) {

        List<HistoricTaskResponse> list = new ArrayList<>();
        List<HistoricTaskInstance> taskList = historyService.createHistoricTaskInstanceQuery()
                .processInstanceId(id).orderByHistoricTaskInstanceEndTime().asc().list();



        Map<String,List<TaskIdAndAssingeModel>> map = new HashMap<>();


        taskList.forEach(e->{

            HistoricTaskInstance historicTaskInstance = e;
            TaskIdAndAssingeModel taskIdAndAssingeModel = new TaskIdAndAssingeModel(e.getId(),e.getAssignee());
            // 流程节点
            String taskDefinitionKey = historicTaskInstance.getTaskDefinitionKey();
            // 流程定义id
            String processDefinitionId = historicTaskInstance.getProcessDefinitionId();
            boolean isHuiqian = ActUtil.isHuiqian(taskDefinitionKey,processDefinitionId);

            if (isHuiqian) {
                if (map.containsKey(e.getTaskDefinitionKey())) {
                    map.get(e.getTaskDefinitionKey()).add(taskIdAndAssingeModel);
                } else {
                    List<TaskIdAndAssingeModel> set = new ArrayList<>();
                    set.add(taskIdAndAssingeModel);
                    map.put(e.getTaskDefinitionKey(),set);
                }
            }

        });


            // 转换vo
            taskList.forEach(e -> {
                HistoricTaskInstance historicTaskInstance = e;
                // 流程节点
                String taskDefinitionKey = historicTaskInstance.getTaskDefinitionKey();
                // 流程定义id
                String processDefinitionId = historicTaskInstance.getProcessDefinitionId();
                boolean isHuiqian = ActUtil.isHuiqian(taskDefinitionKey,processDefinitionId);
                HistoricTaskResponse htv = new HistoricTaskResponse(e);
                List<Assignee> assignees = new ArrayList<>();
                // 关联分配人（委托用户时显示该人）
                if(StrUtil.isNotBlank(htv.getAssignee())){
                    String assignee = zNodeService.getUserByAccount(htv.getAssignee()).getAccount();
                    String owner = "";
                    if (StrUtil.isNotEmpty(htv.getOwner())) {
                        owner = zNodeService.getUserByAccount(htv.getOwner()).getAccount();
                        assignees.add(new Assignee(assignee+"(受"+owner+"委托)", true));
                    }
                }

                if (isHuiqian) {
                    // 会签 关联的人
                    //List<HistoricIdentityLink> identityLinks = historyService.getHistoricIdentityLinksForProcessInstance(e.getProcessInstanceId());
                    // 获取实际审批用户id
                    String userId = zBusinessService.findUserIdByTypeAndTaskId(WorkflowConstants.EXECUTOR_TYPE, e.getId());

                        // 从任务中获取
                        for (Map.Entry<String,List<TaskIdAndAssingeModel>>entry:map.entrySet()) {
                            if (entry.getKey().equals(e.getTaskDefinitionKey())) {
                                List<TaskIdAndAssingeModel> tmpList = entry.getValue();
                                for (TaskIdAndAssingeModel taskIdAndAssingeModel:tmpList) {
                                    Assignee assignee = new Assignee(taskIdAndAssingeModel.getAssigne(), false);
                                    if(StrUtil.isNotBlank(userId)&&userId.equals(taskIdAndAssingeModel.getAssigne())){
                                        assignee.setIsExecutor(true);
                                    }
                                    assignees.add(assignee);
                                }
                            }

                        }
                } else {
                    List<HistoricIdentityLink> identityLinks = historyService.getHistoricIdentityLinksForTask(e.getId());
                    // 获取实际审批用户id
                    String userId = zBusinessService.findUserIdByTypeAndTaskId(WorkflowConstants.EXECUTOR_TYPE, e.getId());
                    for(HistoricIdentityLink hik : identityLinks){
                        // 关联候选用户（分配的候选用户审批人）
                        if("candidate".equals(hik.getType())&& StrUtil.isNotBlank(hik.getUserId())){
                            String username = zNodeService.getUserByAccount(hik.getUserId()).getAccount();
                            Assignee assignee = new Assignee(username, false);
                            if(StrUtil.isNotBlank(userId)&&userId.equals(hik.getUserId())){
                                assignee.setIsExecutor(true);
                            }
                            assignees.add(assignee);
                        }
                    }
                }

                htv.setAssignees(assignees);
                // 关联审批意见
                List<Comment> comments = taskService.getTaskComments(htv.getId(), "comment");
                if(comments!=null&&comments.size()>0){
                    htv.setComment(comments.get(0).getFullMessage());
                }
                list.add(htv);
            });
        return list;
    }


    /***
     * 任务节点审批通过
     * @param id 任务id
     * @param procInstId 流程实例id
     * @param assignees 下个节点审批人
     * @param priority 优先级
     * @param comment 意见评论
     * @param sendMessage 是否发送站内消息
     * @param sendSms 是否发送短信通知
     * @param sendEmail 是否发送邮件通知
     */
    public void pass(String id,
                     String procInstId,
                     String assignees,
                     Integer priority,
                     String comment,
                     Boolean sendMessage,
                     Boolean sendSms,
                     Boolean sendEmail,
                     BpmnUser fromUser) {

        if(StrUtil.isBlank(comment)){
            comment = "";
        }
        taskService.addComment(id, procInstId, comment);
        ProcessInstance pi = runtimeService.createProcessInstanceQuery().processInstanceId(procInstId).singleResult();
        Task task = taskService.createTaskQuery().taskId(id).singleResult();
        if(StrUtil.isNotBlank(task.getOwner())&&!("RESOLVED").equals(task.getDelegationState().toString())){
            // 未解决的委托任务 先resolve
            String oldAssignee = task.getAssignee();
            taskService.resolveTask(id);
            taskService.setAssignee(id, oldAssignee);
        }
        // 启动流程实例把会签的参数都赋值上去。nrOfInstances nrOfActiveInstances nrOfCompletedInstances loopCounter
        Map<String, Object> params = new HashMap<>();
        String [] arr = assignees.split(",");

        params.put("spUserList",Arrays.asList(arr));
        params.put("spUser",Arrays.asList(arr));
        taskService.complete(id,params);
        ActivitiZBusiness actBusiness = zBusinessService.getById(pi.getBusinessKey());
        //修改业务表的流程字段
        zBusinessService.updateBusinessStatus(actBusiness.getTableName(), actBusiness.getTableId(),"审批中-"+task.getName());
        task.getName();
        List<Task> tasks = taskService.createTaskQuery().processInstanceId(procInstId).list();
        // 判断下一个节点
        if(tasks!=null&&tasks.size()>0){

            Task first = tasks.get(0);
            String executionId = first.getExecutionId();
            String processInstanceId = first.getProcessInstanceId();

            // 判断流程图中是否包含并行网关,并行网关中的assignee这个字段没有值，因此走之前逻辑
            String assigneeVal = first.getAssignee();
            boolean singleIns = false;
            if (executionId.equals(processInstanceId)) {
                singleIns = true;
            }
            if (singleIns && (StrUtil.isEmpty(assigneeVal))) {
                for(Task t : tasks){
                    if(StrUtil.isBlank(assignees)){
                        // 如果下个节点未分配审批人为空 取消结束流程
                        List<LoginUser> users = fetchNode(t.getTaskDefinitionKey()).getUsers();
                        if(users==null||users.size()==0){
                            runtimeService.deleteProcessInstance(procInstId, "canceled-审批节点未分配审批人，流程自动中断取消");
                            actBusiness.setStatus(WorkflowConstants.STATUS_CANCELED);
                            actBusiness.setResult(WorkflowConstants.RESULT_TO_SUBMIT);
                            zBusinessService.updateById(actBusiness);
                            //修改业务表的流程字段
                            zBusinessService.updateBusinessStatus(actBusiness.getTableName(), actBusiness.getTableId(),"审批异常-"+task.getName()+"-审批节点未分配审批人，流程自动中断取消");

                            break;
                        }else{
                            // 避免重复添加
                            List<String> list = zBusinessService.selectIRunIdentity(t.getId(), "candidate");
                            if(list==null||list.size()==0) {
                                // 分配了节点负责人分发给全部
                                String assigneeValS = t.getAssignee();
                                if (StrUtil.isEmpty(assigneeValS)) {
                                    for (LoginUser user : users) {
                                        taskService.addCandidateUser(t.getId(), user.getAccount());
                                        // 异步发消息
                                        BpmnUser toUser = zNodeService.getUserByAccount(user.getAccount());
                                        sendActMessage(fromUser,toUser,actBusiness,task.getName(),  sendMessage, sendSms, sendEmail);
                                    }
                                }

                                taskService.setPriority(t.getId(), task.getPriority());
                            }
                        }
                    }else{
                        // 避免重复添加
                        List<String> list = zBusinessService.selectIRunIdentity(t.getId(), "candidate");
                        if(list==null||list.size()==0) {

                            for(String assignee : assignees.split(",")){
                                taskService.addCandidateUser(t.getId(), assignee);
                                // 异步发消息 根据assignee 获取用户
                                BpmnUser toUser = zNodeService.getUserByAccount(assignee);
                                sendActMessage(fromUser,toUser,actBusiness,task.getName(),  sendMessage, sendSms, sendEmail);
                                taskService.setPriority(t.getId(), priority);
                            }
                        }
                    }
                }
            } else {
                for(Task t : tasks){
                    if(StrUtil.isBlank(assignees)){
                        // 如果下个节点未分配审批人为空 取消结束流程
                        List<LoginUser> users = fetchNode(t.getTaskDefinitionKey()).getUsers();
                        if(users==null||users.size()==0){
                            runtimeService.deleteProcessInstance(procInstId, "canceled-审批节点未分配审批人，流程自动中断取消");
                            actBusiness.setStatus(WorkflowConstants.STATUS_CANCELED);
                            actBusiness.setResult(WorkflowConstants.RESULT_TO_SUBMIT);
                            zBusinessService.updateById(actBusiness);
                            //修改业务表的流程字段
                            zBusinessService.updateBusinessStatus(actBusiness.getTableName(), actBusiness.getTableId(),"审批异常-"+task.getName()+"-审批节点未分配审批人，流程自动中断取消");

                            break;
                        }else{
                            // 避免重复添加
                            List<String> list = zBusinessService.selectIRunIdentity(t.getId(), "candidate");
                            if(list==null||list.size()==0) {
                                // 分配了节点负责人分发给全部
                                String assigneeValS = t.getAssignee();
                                if (StrUtil.isEmpty(assigneeValS)) {
                                    for (LoginUser user : users) {
                                        taskService.addCandidateUser(t.getId(), user.getAccount());
                                        // 异步发消息
                                        BpmnUser toUser = zNodeService.getUserByAccount(user.getAccount());
                                        sendActMessage(fromUser,toUser,actBusiness,task.getName(),  sendMessage, sendSms, sendEmail);
                                    }

                                }
                                taskService.setPriority(t.getId(), task.getPriority());

                            }
                        }
                    }else{
                        // 避免重复添加
                        List<String> list = zBusinessService.selectIRunIdentity(t.getId(), "candidate");
                        if(list==null||list.size()==0) {

                            for(String assignee : assignees.split(",")){
                                taskService.addCandidateUser(t.getId(), assignee);
                                // 异步发消息 根据assignee 获取用户
                                BpmnUser toUser = zNodeService.getUserByAccount(assignee);
                                sendActMessage(fromUser,toUser,actBusiness,task.getName(),  sendMessage, sendSms, sendEmail);
                                taskService.setPriority(t.getId(), priority);
                            }
                        }
                    }
                    // 避免消息落库重复和消息重复发
                    break;
                }
            }

        } else {
            actBusiness.setStatus(WorkflowConstants.STATUS_FINISH);
            actBusiness.setResult(WorkflowConstants.RESULT_PASS);
            zBusinessService.updateById(actBusiness);
            // 异步发消息
            // 根据账户获取用户信息
            BpmnUser toUser = zNodeService.getUserByAccount(actBusiness.getUserId());
            sendMessage(fromUser,toUser,WorkflowConstants.MESSAGE_PASS_CONTENT,
                    String.format("您的 【%s】 申请已通过！",actBusiness.getTitle()),sendMessage, sendSms, sendEmail);
            //修改业务表的流程字段
            zBusinessService.updateBusinessStatus(actBusiness.getTableName(), actBusiness.getTableId(),"审批通过");

        }
        // 记录实际审批人员
        zBusinessService.insertHiIdentityLink(IdUtil.simpleUUID(),
                WorkflowConstants.EXECUTOR_TYPE, fromUser.getAccount(), id, procInstId);


    }




    /***
     * 委托他人代办
     * @param id 任务id
     * @param userId 委托用户id
     * @param procInstId 流程实例id
     * @param comment 意见评论
     * @param sendMessage 是否发送站内消息
     * @param sendSms 是否发送短信通知
     * @param sendEmail 是否发送邮件通知
     */
    public void delegate(String id,
                         String userId,
                         String procInstId,
                         String comment,
                         Boolean sendMessage,
                         Boolean sendSms,
                         Boolean sendEmail,
                         BpmnUser formUser) {

        if(StrUtil.isBlank(comment)){
            comment = "";
        }
        taskService.addComment(id, procInstId, comment);
        taskService.delegateTask(id, userId);
        taskService.setOwner(id, formUser.getAccount());
        // 异步发消息
        BpmnUser toUser = zNodeService.getUserByAccount(userId);
        sendMessage(formUser,toUser,WorkflowConstants.MESSAGE_DELEGATE_CONTENT,
                String.format("您有一个来自 %s 的委托需要处理！",formUser.getAccount()),sendMessage, sendSms, sendEmail);
    }


    /***
     * 任务节点审批驳回至指定历史节点
     * @param id 任务id
     * @param backTaskKey 驳回指定节点key
     * @param procInstId 流程实例id
     * @param procDefId 流程定义id
     * @param assignees 原节点审批人
     * @param priority 优先级
     * @param comment 意见评论
     * @param sendMessage 是否发送站内消息
     * @param sendSms 是否发送短信通知
     * @param sendEmail 是否发送邮件通知
     * @param fromUser 当前登录人->bpmnUser
     */
    public void backToTask( String id,
                            String backTaskKey,
                            String procInstId,
                            String procDefId,
                            String assignees,
                            Integer priority,
                            String comment,
                            Boolean sendMessage,
                            Boolean sendSms,
                            Boolean sendEmail,
                            BpmnUser fromUser) {
        if(StrUtil.isBlank(comment)){
            comment = "";
        }
        taskService.addComment(id, procInstId, comment);
        // 取得流程定义
        ProcessDefinitionEntity definition = (ProcessDefinitionEntity) repositoryService.getProcessDefinition(procDefId);
        // 获取历史任务的Activity
        ActivityImpl hisActivity = definition.findActivity(backTaskKey);
        // 实现跳转
        managementService.executeCommand(new JumpTask(procInstId, hisActivity.getId()));
        // 重新分配原节点审批人
        List<Task> tasks = taskService.createTaskQuery().processInstanceId(procInstId).list();
        if(tasks!=null&&tasks.size()>0){
            tasks.forEach(e->{
                for(String assignee:assignees.split(",")){
                    taskService.addCandidateUser(e.getId(), assignee);
                    // 异步发消息
                    // 根据账户信息获取参与人
                    BpmnUser bpmnUser = zNodeService.getUserByAccount(assignee);
//                    BpmnUser formUser = new BpmnUser();
//                    BeanUtil.copyProperties(loginUser,formUser,false);
                    sendMessage(fromUser,bpmnUser,WorkflowConstants.MESSAGE_TODO_CONTENT
                            ,"您有一个任务待审批，请尽快处理！",sendMessage, sendSms, sendEmail);
                }
                if(priority!=null){
                    taskService.setPriority(e.getId(), priority);
                }
            });
        }
        // 记录实际审批人员
        zBusinessService.insertHiIdentityLink(IdUtil.simpleUUID(),
                WorkflowConstants.EXECUTOR_TYPE, fromUser.getAccount(), id, procInstId);

    }

    public void editFormData(String tableId, Map<String, Object> paramMap) {
        zBusinessService.editFormData(tableId,paramMap);
    }

    public ProcessNode getFirstTaskNode(String procDefId, String tableId, String tableName) {
        BpmnModel bpmnModel = repositoryService.getBpmnModel(procDefId);

        ProcessNode node = new ProcessNode();

        List<Process> processes = bpmnModel.getProcesses();
        Collection<FlowElement> elements = processes.get(0).getFlowElements();
        // 流程开始节点
        StartEvent startEvent = null;
        for (FlowElement element : elements) {
            if (element instanceof StartEvent) {
                startEvent = (StartEvent) element;
                break;
            }
        }
        FlowElement e = null;
        if (startEvent.getOutgoingFlows().size()>0) {
            // 开始节点后不止一个用户任务节点
            List<SequenceFlow> sfs = startEvent.getOutgoingFlows();
            for (SequenceFlow sf:sfs) {
                String el = sf.getConditionExpression();
                if (StrUtil.isEmptyIfStr(el)) {// 如果连线上没有配置表达式，默认选取一个
                    String targetId = sf.getTargetRef();
                    for (FlowElement element : elements) {
                        if(element.getId().equals(targetId)){
                            if(element instanceof UserTask){
                                e = element;
                                node.setType(1);
                                break;
                            }else if(element instanceof ExclusiveGateway){
                                e = element;
                                node.setType(3);
                                break;
                            }else if(element instanceof ParallelGateway){
                                e = element;
                                node.setType(4);
                                break;
                            }else{
                                throw new RuntimeException("流程设计错误，开始节点后只能是用户任务节点、排他网关、并行网关");
                            }
                        }
                    }
                    // 排他
                    if(e instanceof ExclusiveGateway){
                        // 如果是网关类型需要根据网关上配置的条件继续寻找下一个用户任务节点
                        ExclusiveGateway exclusiveGateway = (ExclusiveGateway)e;
                        List<SequenceFlow> sequenceFlows = exclusiveGateway.getOutgoingFlows();
                        // 获取所有的线
                        for (SequenceFlow sequenceFlow:sequenceFlows) {
                            String conditionExpression = sequenceFlow.getConditionExpression();
                            boolean rst = isElCondition(tableId,conditionExpression,tableName);
                            if (rst) {//判断条件通过
                                String tarId = sequenceFlow.getTargetRef();
                                for (FlowElement element : elements) {
                                    if(element.getId().equals(tarId)){
                                        if(element instanceof UserTask){
                                            e = element;
                                            node.setType(1);
                                            break;
                                        }else if(element instanceof ExclusiveGateway){
                                            e = element;
                                            node.setType(3);
                                            break;
                                        }else if(element instanceof ParallelGateway){
                                            e = element;
                                            node.setType(4);
                                            break;
                                        }else{
                                            throw new RuntimeException("流程设计错误，开始节点后只能是用户任务节点、排他网关、并行网关");
                                        }
                                    }
                                }
                                // 排他、平行网关直接返回
                                if(e instanceof ExclusiveGateway || e instanceof ParallelGateway){
                                    return node;
                                }
                                node.setTitle(e.getName());
                                // 设置关联用户
                                List<LoginUser> users = getNodetUsers(e.getId());
                                node.setUsers(removeDuplicate(users));
                                //设置关联角色
                                //node.setRoles(zNodeService.findRoleByNodeId(e.getId()));
                            }
                        }
                        return node;
                    }

                    // 平行网关直接返回
                    if (e instanceof ParallelGateway) {
                        return node;
                    }

                    node.setTitle(e.getName());
                    // 设置关联用户
                    List<LoginUser> users = getNodetUsers(e.getId());
                    node.setUsers(removeDuplicate(users));
                    //设置关联角色
                    //node.setRoles(zNodeService.findRoleByNodeId(e.getId()));
                    break;
                } else {
                    boolean result = isElCondition(tableId,el,tableName);
                    if (result) {//判断条件通过
                        String targetId = sf.getTargetRef();
                        for (FlowElement element : elements) {
                            if(element.getId().equals(targetId)){
                                if(element instanceof UserTask){
                                    e = element;
                                    node.setType(1);
                                    break;
                                }else if(element instanceof ExclusiveGateway){
                                    e = element;
                                    node.setType(3);
                                    break;
                                }else if(element instanceof ParallelGateway){
                                    e = element;
                                    node.setType(4);
                                    break;
                                }else{
                                    throw new RuntimeException("流程设计错误，开始节点后只能是用户任务节点、排他网关、并行网关");
                                }
                            }
                        }
                        // 排他、平行网关直接返回
                        if(e instanceof ExclusiveGateway || e instanceof ParallelGateway){
                            return node;
                        }
                        node.setTitle(e.getName());
                        // 设置关联用户
                        List<LoginUser> users = getNodetUsers(e.getId());
                        node.setUsers(removeDuplicate(users));
                        // 设置关联角色
                       // node.setRoles(zNodeService.findRoleByNodeId(e.getId()));
                    }
                }

            }
            return node;
        }
        // 判断开始后的流向节点
        SequenceFlow sequenceFlow = startEvent.getOutgoingFlows().get(0);
        for (FlowElement element : elements) {
            if(element.getId().equals(sequenceFlow.getTargetRef())){
                if(element instanceof UserTask){
                    e = element;
                    node.setType(1);
                    break;
                }else if(element instanceof ExclusiveGateway){
                    e = element;
                    node.setType(3);
                    break;
                }else if(element instanceof ParallelGateway){
                    e = element;
                    node.setType(4);
                    break;
                }else{
                    throw new RuntimeException("流程设计错误，开始节点后只能是用户任务节点、排他网关、并行网关");
                }
            }
        }
        // 排他、平行网关直接返回
        if(e instanceof ExclusiveGateway || e instanceof ParallelGateway){
            return node;
        }
        node.setTitle(e.getName());
        // 设置关联用户
        List<LoginUser> users = getNodetUsers(e.getId());
        node.setUsers(removeDuplicate(users));
        return node;

    }

    public class JumpTask implements Command<ExecutionEntity> {

        private String procInstId;
        private String activityId;

        public JumpTask(String procInstId, String activityId) {
            this.procInstId = procInstId;
            this.activityId = activityId;
        }

        @Override
        public ExecutionEntity execute(CommandContext commandContext) {

            ExecutionEntity executionEntity = commandContext.getExecutionEntityManager().findExecutionById(procInstId);
            executionEntity.destroyScope("backed");
            ProcessDefinitionImpl processDefinition = executionEntity.getProcessDefinition();
            ActivityImpl activity = processDefinition.findActivity(activityId);
            executionEntity.executeActivity(activity);

            return executionEntity;
        }

    }

    /***
     * 已办列表
     * @param name 查询条件
     * @param categoryId 查询条件
     * @param priority 查询条件
     * @param appId 所属应用
     * @param loginUser 当前登录人
     * @param allUser 所有人
     * @return
     */
    public PageResult<HistoricTaskResponse> doneList(String name,
                                               String categoryId,
                                               Integer priority,
                                               String appId,
                                               LoginUser loginUser,
                                               List<ComboModel> allUser,int firstResult, int maxResults) {
        List<HistoricTaskResponse> list = new ArrayList<>();
        String userId = loginUser.getAccount();
        HistoricTaskInstanceQuery query = historyService.createHistoricTaskInstanceQuery().or().taskCandidateUser(userId).
                taskAssignee(userId).endOr().finished();
        // 多条件搜索
        query.orderByTaskCreateTime().desc();
        if(StrUtil.isNotBlank(name)){
            query.taskNameLike("%"+name+"%");
        }
        if(StrUtil.isNotBlank(categoryId)){
            query.taskCategory(categoryId);
        }
        if(priority!=null){
            query.taskPriority(priority);
        }
        List<HistoricTaskInstance> taskList = query.listPage((firstResult-1)*maxResults,maxResults);
        Map<String, String> userMap = allUser.stream().collect(Collectors.toMap(ComboModel::getUsername, ComboModel::getTitle));
        taskList.forEach(e -> {
            HistoricTaskResponse htv = new HistoricTaskResponse(e);
            // 关联委托人
            if(StrUtil.isNotBlank(htv.getOwner())){
                htv.setOwner(userMap.get(htv.getOwner()));
            }
            List<HistoricIdentityLink> identityLinks = historyService.getHistoricIdentityLinksForProcessInstance(htv.getProcInstId());
            for(HistoricIdentityLink hik : identityLinks){
                // 关联发起人
                if("starter".equals(hik.getType())&&StrUtil.isNotBlank(hik.getUserId())){
                    htv.setApplyer(userMap.get(hik.getUserId()));
                }
            }
            // 关联审批意见
            List<Comment> comments = taskService.getTaskComments(htv.getId(), "comment");
            if(comments!=null&&comments.size()>0){
                htv.setComment(comments.get(0).getFullMessage());
            }
            // 关联流程信息
            ActivitiZProcess actProcess = zProcessService.getById(htv.getProcDefId());
            if(actProcess!=null){
                htv.setProcessName(actProcess.getName());
                htv.setRouteName(actProcess.getRouteName());
                htv.setType(actProcess.getType());
            }
            // 关联业务key
            HistoricProcessInstance hpi = historyService.createHistoricProcessInstanceQuery().processInstanceId(htv.getProcInstId()).singleResult();
            htv.setBusinessKey(hpi.getBusinessKey());
            ActivitiZBusiness actBusiness = zBusinessService.getById(hpi.getBusinessKey());
            if(actBusiness!=null){
                htv.setTableId(actBusiness.getTableId());
                htv.setTableName(actBusiness.getTableName());
                //关联业务所属应用id
                htv.setAppId(actBusiness.getAppid());
            }

            list.add(htv);
        });

        if(StrUtil.isNotBlank(appId)) {
            //根据应用id过滤我的已办信息
            List<HistoricTaskResponse> rst = new ArrayList<>();

            for (HistoricTaskResponse historicTaskVo : list) {
                if (appId.equals(historicTaskVo.getAppId())) {
                    rst.add(historicTaskVo);
                }
            }
        }
        Page<HistoricTaskResponse> page = new Page<>();
        page.setRecords(list);
        page.setTotal(query.count());
        return PageResultFactory.createPageResult(page);
    }

    /***
     * 删除任务历史
     * @param ids 逗号分割
     */
    public void deleteHistoric(String ids) {
        for(String id : ids.split(",")){
            historyService.deleteHistoricTaskInstance(id);
        }
    }

    //////////////////////////////////////流程任务-结束/////////////////////////////////////////////////



    //////////////////////////////////////流程消息-开始/////////////////////////////////////////////////

    @Override
    public String parseTemplateByCode(NewsTemplate newsTemplate) {
        String templateCode = newsTemplate.getTemplateCode();
        Map<String,String> param = newsTemplate.getTemplateParam();
        List<ActivitiZNewsTemplate> templates = zNewsTemplateService.selectByCode(templateCode);
        if (templates == null || templates.size() == 0) {
            throw new RuntimeException("消息模板不存在，模板编码:"+templateCode);
        }
        ActivitiZNewsTemplate activitiZNewsTemplate = templates.get(0);
        // 消息内容
        String templateContent = activitiZNewsTemplate.getTemplateContent();
        if (param != null) {
            for (Map.Entry<String, String> entry : param.entrySet()) {
                String str = "${" + entry.getKey() + "}";
                templateContent = templateContent.replace(str, entry.getValue());
            }
        }
        return templateContent;
    }


    //////////////////////////////////////流程消息-结束/////////////////////////////////////////////////



    /////////////////////////////////////入口管理-开始//////////////////////////////////////////////////
    /**入口管理修改*/
    public void edit(ActEntranceRequest actEntranceRequest) {
        actEntranceService.edit(actEntranceRequest);
    }
    /**入口管理新增*/
    public void add(ActEntranceRequest actEntranceRequest) {
        actEntranceService.add(actEntranceRequest);
    }
    /**入口管理删除*/
    public void del(ActEntranceRequest actEntranceRequest) {
        actEntranceService.del(actEntranceRequest);
    }
    /**入口管理查询详情*/
    public ActEntranceDTO detail(ActEntranceRequest actEntranceRequest) {
        return actEntranceService.detail(actEntranceRequest);
    }
    /**入口管理分页查询列表*/
    public PageResult<ActEntranceDTO> findPage(ActEntranceRequest actEntranceRequest) {
        return actEntranceService.findPage(actEntranceRequest);
    }
    /**入口管理发起申请查询*/
    public Map<String, List<ActEntranceDTO>> findList(ActEntranceRequest actEntranceRequest) {
        List<ActEntranceDTO> list = actEntranceService.findList(actEntranceRequest);
        Map<String, List<ActEntranceDTO>> map = list.stream().collect(Collectors.groupingBy(ActEntranceDTO::getCategoryName));
        return map;
    }

    /////////////////////////////////////入口管理-结束//////////////////////////////////////////////////




    /////////////////////////////////////流程表达式设置-开始//////////////////////////////////////////////////
    public List<ActivitiExpression> getExpression(ActiviExpressionRequest activiExpressionRequest) {
        return activiExpressionService.getExpression(activiExpressionRequest);
    }

    /////////////////////////////////////流程表达式设置-结束//////////////////////////////////////////////////






    /////////////////////////////////////流程定义设置表单-结束//////////////////////////////////////////////////

    public void addActFrom(ActFromRequest actFromRequest) {
        fromService.addActFrom(actFromRequest);
    }

    public void editActFrom(ActFromRequest actFromRequest) {
        fromService.editActFrom(actFromRequest);
    }

    public PageResult<ActFromDTO> getFromPage(ActFromRequest actFromRequest) {
        return fromService.getFromPage(actFromRequest);
    }

    public ActFromDTO getActFormById(String id) {
        return fromService.getActFormById(id);
    }

    /////////////////////////////////////流程定义设置表单-结束//////////////////////////////////////////////////




    ////////////////////////////////////流程定义设置按钮权限-开始//////////////////////////////////////////////////

    public void addButton(ActButtonRequest actButtonRequest) {
        activiButtonService.addButton(actButtonRequest);
    }

    public List<ActButton> selectButton(ActButtonRequest actButtonRequest) {
        return activiButtonService.getFromPage(actButtonRequest);
    }

    ////////////////////////////////////流程定义设置按钮权限-结束//////////////////////////////////////////////////




    /////////////////////////////////////配置表单-开始//////////////////////////////////////////////////

    /***
     * 根据表单的编码和流程定义id和流程任务id查询表单中都有哪些授权字段
     * @param processId 流程id
     * @param taskId 流程任务id
     * @return
     */
    public List<ActivitiZNodeAuth> fetchActFormColByTaskId(String processId,String taskId,String formId){
        LambdaQueryWrapper<ActivitiZNodeAuth> queryWrapper = new LambdaQueryWrapper<>();
        // SQL条件拼接
        queryWrapper.eq(ObjectUtil.isNotNull(processId), ActivitiZNodeAuth::getProcessId, processId);
        queryWrapper.eq(ObjectUtil.isNotNull(taskId), ActivitiZNodeAuth::getTaskId, taskId);
        queryWrapper.eq(ObjectUtil.isNotNull(formId), ActivitiZNodeAuth::getFormBizCode, formId);
        List<ActivitiZNodeAuth> res = zNodeAuthService.list(queryWrapper);
        return res;
    }

    /***
     * 新增
     * @param request
     */
    public void saveActZNodeAuth (ActivitiZNodeAuthRequest request) {
        zNodeAuthService.saveActZNodeAuth(request);
    }

    /***
     * 修改
     * @param request
     */
    public void modifyActZNodeAuth (ActivitiZNodeAuthRequest request) {
        ActivitiZNodeAuth nodeAuth = new ActivitiZNodeAuth();
        BeanUtil.copyProperties(request,nodeAuth);
        zNodeAuthService.updateById(nodeAuth);
    }


    /***
     * 根据表单的编码和流程定义id和流程任务id查询表单中都有哪些授权字段
     * @param processId 流程id
     * @param taskId 流程任务id
     * @return
     */
    @Override
    public Map<String, String> fetchActColByTaskId(String processId,String taskId,String formId){
        LambdaQueryWrapper<ActivitiZNodeAuth> queryWrapper = new LambdaQueryWrapper<>();
        // SQL条件拼接
        queryWrapper.eq(ObjectUtil.isNotNull(processId), ActivitiZNodeAuth::getProcessId, processId);
        queryWrapper.eq(ObjectUtil.isNotNull(taskId), ActivitiZNodeAuth::getTaskId, taskId);
        queryWrapper.eq(ObjectUtil.isNotNull(formId), ActivitiZNodeAuth::getFormBizCode, formId);
        List<ActivitiZNodeAuth> res = zNodeAuthService.list(queryWrapper);
        Map<String, String> map = new HashMap<>();
        res.forEach(itm->{
            map.put(itm.getDesformComKey(),itm.getRuleType());
        });
        return map;
    }


    /////////////////////////////////////配置表单-结束//////////////////////////////////////////////////








}
